1 INTRODUCTION

The Framingham Heart study started in 1948 to identify factors or characteristics that contribute to cardiovascular disease. The study is based in Massachusetts and has followed multiple generations of participants. The study is a longitudinal design that collects data via examinations and health assessments. As well, participants undergo medical evaluations, laboratory tests, and lifestyle assessments. Some of these risk factors are behavioral and some are biological. This analysis considers a data subset of the Framingham Heart Study.

This analysis will examine the association of some of the variables that are associated with diabetes and smoking. As well, investigation into how some of these factors predict the likelihood of developing coronary heart disease within ten years among participants in the Framingham Heart Study will be conducted.

2 FRAMINGHAM DATA SUBSET

2.1 Description of Data

The Framingham Heart Study includes data from about 15,000 participants and over 1,000 variables. The data set used for this analysis is a subset containing 4240 observations and 16 variables. Of the 16 variables, 8 variables are factor variables and 7 are numeric variables.

The list of variable names and their description/definitions in this data subset is listed below.

  1. sex (factor): the sex of the observations. The variable is a binary named “male” in the dataset.
  2. age (numerical): Age at the time of medical examination in years.
  3. education (factor): A categorical variable of the participants education, with the levels: Some high school (1), high school/GED (2), some college/vocational school (3), college (4)
  4. currentSmoker (factor): Current cigarette smoking at the time of examinations
  5. cigsPerDay (numerical): Number of cigarettes smoked each day
  6. BPmeds (factor): Use of Anti-hypertensive medication at exam
  7. prevalentStroke (factor): Prevalent Stroke (0 = free of disease)
  8. prevalentHyp (factor): Prevalent Hypertensive. Subject was defined as hypertensive if treated
  9. diabetes (factor): Diabetic according to criteria of first exam treated
  10. totChol (numerical): Total cholesterol (mg/dL)
  11. sysBP (numerical): Systolic Blood Pressure (mmHg)
  12. diaBP (numerical): Diastolic blood pressure (mmHg)
  13. BMI (numerical): Body Mass Index, weight (kg)/height (m)^2
  14. heartRate (numerical): Heart rate (beats/minute)
  15. glucose (numerical): Blood glucose level (mg/dL)
  16. TenYearCHD (factor, response variable): The 10 year risk of coronary heart disease(CHD).
heartdata <- read.csv("https://raw.githubusercontent.com/jmusa3/jmusa/refs/heads/main/FraminghamHeartStudy.csv")


# Convert some variables to factors
heartdata <- heartdata %>%
  mutate(
    male = as.factor(male),
    education = as.factor(education),
    currentSmoker = as.factor(currentSmoker),
    BPMeds = as.factor(BPMeds),
    prevalentStroke = as.factor(prevalentStroke),
    prevalentHyp = as.factor(prevalentHyp),
    diabetes = as.factor(diabetes),
    TenYearCHD = as.factor(TenYearCHD)
  )
summary(heartdata)
 male          age        education   currentSmoker   cigsPerDay    
 0:2420   Min.   :32.00   1   :1720   0:2145        Min.   : 0.000  
 1:1820   1st Qu.:42.00   2   :1253   1:2095        1st Qu.: 0.000  
          Median :49.00   3   : 689                 Median : 0.000  
          Mean   :49.58   4   : 473                 Mean   : 9.006  
          3rd Qu.:56.00   NA's: 105                 3rd Qu.:20.000  
          Max.   :70.00                             Max.   :70.000  
                                                    NA's   :29      
  BPMeds     prevalentStroke prevalentHyp diabetes    totChol     
 0   :4063   0:4215          0:2923       0:4131   Min.   :107.0  
 1   : 124   1:  25          1:1317       1: 109   1st Qu.:206.0  
 NA's:  53                                         Median :234.0  
                                                   Mean   :236.7  
                                                   3rd Qu.:263.0  
                                                   Max.   :696.0  
                                                   NA's   :50     
     sysBP           diaBP            BMI          heartRate     
 Min.   : 83.5   Min.   : 48.0   Min.   :15.54   Min.   : 44.00  
 1st Qu.:117.0   1st Qu.: 75.0   1st Qu.:23.07   1st Qu.: 68.00  
 Median :128.0   Median : 82.0   Median :25.40   Median : 75.00  
 Mean   :132.4   Mean   : 82.9   Mean   :25.80   Mean   : 75.88  
 3rd Qu.:144.0   3rd Qu.: 90.0   3rd Qu.:28.04   3rd Qu.: 83.00  
 Max.   :295.0   Max.   :142.5   Max.   :56.80   Max.   :143.00  
                                 NA's   :19      NA's   :1       
    glucose       TenYearCHD
 Min.   : 40.00   0:3596    
 1st Qu.: 71.00   1: 644    
 Median : 78.00             
 Mean   : 81.96             
 3rd Qu.: 87.00             
 Max.   :394.00             
 NA's   :388                
# Find the total number of observations (rows) in heartdata
#total_observations <- nrow(heartdata)
#total_observations

# Find number of observations that have at least one missing value
#missing_rows <- apply(heartdata, 1, function(x) any(is.na(x)))

#sum(missing_rows)

Table 1. Summary statistics for the 16 variables in the Framingham Heart Study subset. Seven of the variables have missing values. Male, education, currentSmoker, BPMeds, prevalentSmoker, prevalentHyp, diabetes, and TenYearCHD were converted to factor variables.

2.2 Missing Values

The data subset contained missing values. Table 2 displays the counts for missing values. There were 105 missing values for education, 29 missing values for cigsPerDay, 53 missing values for BPMeds, 50 missing values for totChol, 19 missing values for BMI, 1 missing value for heartRate, and 388 missing values for glucose. Of the 4,240 observations in this data subset, 582 observations had at least one missing value.

Several steps were taken to impute missing values.

# Count missing values for each variable 
missing_values <- colSums(is.na(heartdata))

# Display the result
print(missing_values)
           male             age       education   currentSmoker      cigsPerDay 
              0               0             105               0              29 
         BPMeds prevalentStroke    prevalentHyp        diabetes         totChol 
             53               0               0               0              50 
          sysBP           diaBP             BMI       heartRate         glucose 
              0               0              19               1             388 
     TenYearCHD 
              0 
#education has 105, cigsPerDay has 29, totChol has 50, BPMeds has 53, BMI has 19, heartRate has 1, glucose has 388 missing values

#number of rows before subsetting
#num_observations <- nrow(heartdata)
#print(num_observations)
#ds has 4240 rows

Table 2. A count of missing values by variables.

First, the data was subsetted to include the 3,658 complete observations. From the complete data set, a multinomial regression model was created. The model then predicted values and imputed these predicted values for missing education data in the original data set. After this process was completed, there were 3,744 observations with no missing values.

# Subset the dataset to remove rows with any missing values to create regression models for missing values

heart_clean <- na.omit(heartdata)

# Display the cleaned dataset
#print(heart_clean)

#number of rows after subsetting
#num_observations <- nrow(heart_clean)
#print(num_observations)


#MULTINOMIAL REGRESSION FOR EDUCATION MISSING VALUES 

# Convert education to a factor
heart_clean$education <- as.factor(heart_clean$education)

# Check for missing values in education
#table(is.na(heart_clean$education))

# Fit the multinomial logistic regression model
model_multinomial <- multinom(education ~ age + male + glucose + cigsPerDay, data = heart_clean)
# weights:  24 (15 variable)
initial  value 5071.064773 
iter  10 value 4687.892616
iter  20 value 4514.528726
final  value 4514.520520 
converged
# Summary of the model
summary(model_multinomial)
Call:
multinom(formula = education ~ age + male + glucose + cigsPerDay, 
    data = heart_clean)

Coefficients:
  (Intercept)         age      male1       glucose   cigsPerDay
2    3.077944 -0.06562730 -0.2933894 -0.0007032695  0.003254855
3    1.436378 -0.04270191 -0.5100231  0.0002438273 -0.002255328
4    1.365098 -0.05179202  0.5409309 -0.0034304339 -0.007224380

Std. Errors:
  (Intercept)         age      male1     glucose  cigsPerDay
2   0.2846854 0.005061389 0.08750179 0.001759866 0.003650970
3   0.3326750 0.005921283 0.10645781 0.001963602 0.004575652
4   0.4007450 0.006834408 0.11827156 0.002782473 0.004898821

Residual Deviance: 9029.041 
AIC: 9059.041 
# Get the coefficients
coef(model_multinomial)
  (Intercept)         age      male1       glucose   cigsPerDay
2    3.077944 -0.06562730 -0.2933894 -0.0007032695  0.003254855
3    1.436378 -0.04270191 -0.5100231  0.0002438273 -0.002255328
4    1.365098 -0.05179202  0.5409309 -0.0034304339 -0.007224380
# Calculate odds ratios
odds_ratios <- exp(coef(model_multinomial))
odds_ratios
  (Intercept)       age     male1   glucose cigsPerDay
2   21.713707 0.9364798 0.7457317 0.9992970  1.0032602
3    4.205437 0.9581970 0.6004817 1.0002439  0.9977472
4    3.916107 0.9495263 1.7176050 0.9965754  0.9928017
# Create a logical vector to identify missing education values
missing_rows <- is.na(heartdata$education)

# Create newdata for prediction (only include rows without missing education values)
newdata <- heartdata[!missing_rows,]

# Predict education levels
predicted_classes <- predict(model_multinomial, newdata = newdata)

# Fill in the missing education values in heartdata
heartdata$education[missing_rows] <- predicted_classes

# View the updated heartdata
#head(heartdata)
#summary(heartdata)

#no missing values for education

Second, a logistic regression model was created used the complete data set to predict BPMeds. The model was used to impute the missing BPMeds data in the original data set.

#LOGISTICAL REGRESSION TO IMPUTE MISSING BPMEDS

# Step 1: Identify missing rows in BPMeds
missing_rows <- is.na(heartdata$BPMeds)

# Step 2: Create newdata for prediction (only include rows without missing BPMeds)
# Use complete cases for fitting the model from heart_clean
complete_cases <- heart_clean[!is.na(heart_clean$BPMeds), ]

# Step 3: Fit the logistic regression model (assuming you have relevant predictors)
model_logistic <- glm(BPMeds ~ age + male + prevalentHyp + sysBP + diaBP, 
                      data = complete_cases, 
                      family = binomial())

# Step 4: Predict for all cases in heartdata including the missing ones
# Use heartdata for prediction, ensuring it includes all relevant predictors
predicted_probs <- predict(model_logistic, newdata = heartdata, type = "response")

# Step 5: Classify probabilities into classes (0 or 1)
predicted_classes <- ifelse(predicted_probs > 0.5, 1, 0)

# Step 6: Fill in the missing BPMeds values using the predictions
# Make sure you only fill in the missing values
heartdata$BPMeds[missing_rows] <- predicted_classes[missing_rows]

# Step 7: Check the results
#head(heartdata)
#cat("Remaining missing values in BPMeds:", sum(is.na(heartdata$BPMeds)), "\n")

#summary(heartdata)

#END LOGISTIC REGRESSION

Third, regression models were then created from the completed data to predict missing values for cigsPerDay, heartRate, BMI, and glucose.

Since systolic blood pressure is linearly correlated with BMI (Figure A), a model was created to predict the missing values for BMI using the log of systolic blood pressure as the predictor.

# Step 1: Set the seed for reproducibility
set.seed(123)

# Step 2: Generate a 10% bootstrap sample from heart_clean
sample_size <- floor(0.1 * nrow(heart_clean))  # 10% of the data
bootstrap_sample <- heart_clean[sample(1:nrow(heart_clean), size = sample_size, replace = TRUE), ]

# Step 3: Create a scatter plot for BMI and sysBP in the bootstrap sample
library(ggplot2)
ggplot(bootstrap_sample, aes(x = sysBP, y = BMI)) +
  geom_point() +
  labs(title = "Scatterplot of BMI vs systolic Blood Pressure (10% Bootstrap Sample)",
       x = "Systolic Blood Pressure",
       y = "BMI") +
  theme_minimal()

# Q-Q Plot for systolic BP
ggplot(bootstrap_sample, aes(sample = sysBP)) +
  stat_qq() +
  stat_qq_line(col = "red") +
  labs(title = "Q-Q Plot of Systolic Blood Pressure (Bootstrap Sample)",
       x = "Theoretical Quantiles",
       y = "Sample Quantiles") +
  theme_minimal()

# Step 1: Build the linear regression model to predict log(BMI) based on log(sysBP)
model_BMI <- lm(log(BMI) ~ log(sysBP), data = heart_clean)

# Step 2: Check the summary of the model
summary(model_BMI)

Call:
lm(formula = log(BMI) ~ log(sysBP), data = heart_clean)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.52456 -0.09218 -0.00305  0.08869  0.68173 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.67713    0.07337   22.86   <2e-16 ***
log(sysBP)   0.32031    0.01505   21.28   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1436 on 3656 degrees of freedom
Multiple R-squared:  0.1102,    Adjusted R-squared:   0.11 
F-statistic: 452.9 on 1 and 3656 DF,  p-value: < 2.2e-16
# Step 3: Identify rows with missing BMI values in heartdata
missing_BMI_rows <- is.na(heartdata$BMI)

# Step 4: Predict log-transformed BMI values for missing rows
predicted_log_BMI <- predict(model_BMI, newdata = heartdata[missing_BMI_rows, ])

# Step 5: Exponentiate the predicted log(BMI) values to get predictions in the original BMI scale
predicted_BMI <- exp(predicted_log_BMI)

# Step 6: Impute the predicted BMI values into the missing positions in heartdata
heartdata$BMI[missing_BMI_rows] <- predicted_BMI

# Step 7: Check the imputed values
summary(heartdata$BMI)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  15.54   23.08   25.40   25.80   28.04   56.80 

Figure A. The bootstrap scatterplot of BMI and systolic blood pressure displays a moderate linear relationship. The QQ plot indicates that the distribution of systolic blood pressure is not normal. Hence, the log of systolic blood pressure is used as the predictor.

Glucose and BMI are linearly correlated (Figure B). Therefore, a model using log of BMI to predict glucose was created to impute missing glucose data.

#REGRESSION: IMPUTE MISSING GLUCOSE USING BMI AS A PREDICTOR

#BOOTSTRAP SAMPLE & SCATTERPLOT FOR GLUCOSE AND BMI TO ASSESS LINEAR RELATIONSHIP

# Step 1: Set the seed for reproducibility
set.seed(567)

# Step 2: Generate a smaller bootstrap sample (e.g., 1% of the original size)
sample_size <- floor(0.1 * nrow(heart_clean))  
bootstrap_sample_small <- heart_clean[sample(1:nrow(heart_clean), size = sample_size, replace = TRUE), ]


# Step 3: Create a scatterplot for the bootstrap sample
ggplot(bootstrap_sample_small, aes(x = BMI, y = glucose)) +
  geom_point() +
  labs(title = "Scatterplot of Glucose vs BMI (Bootstrap Sample)",
       x = "BMI",
       y = "Glucose") +
  theme_minimal()

qqnorm(bootstrap_sample_small$BMI, main = "Q-Q Plot for BMI (Bootstrap Sample)")
qqline(bootstrap_sample_small$BMI, col = "red")

# Step 1: Remove any observations with ANY missing values
heartdata_clean2 <- na.omit(heartdata)

# Step 2: Fit a linear regression model to predict log(glucose) based on log(BMI) using the cleaned dataset
glucose_model <- lm(log(glucose) ~ log(BMI), data = heartdata_clean2)

# Step 3: Check the summary of the model
summary(glucose_model)

Call:
lm(formula = log(glucose) ~ log(BMI), data = heartdata_clean2)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.74624 -0.11352 -0.02055  0.08046  1.64765 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  3.95854    0.07053  56.126  < 2e-16 ***
log(BMI)     0.13020    0.02175   5.985 2.36e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.2042 on 3808 degrees of freedom
Multiple R-squared:  0.009319,  Adjusted R-squared:  0.009059 
F-statistic: 35.82 on 1 and 3808 DF,  p-value: 2.362e-09
# Step 4: Identify rows with missing glucose values in heartdata
missing_glucose_rows <- is.na(heartdata$glucose)

# Step 5: Predict log-transformed glucose values for missing rows
predicted_log_glucose <- predict(glucose_model, newdata = heartdata[missing_glucose_rows, ])

# Step 6: Exponentiate the predicted log(glucose) values to get predictions in the original scale
predicted_glucose <- exp(predicted_log_glucose)

# Step 7: Impute the predicted glucose values into the missing positions in heartdata
heartdata$glucose[missing_glucose_rows] <- predicted_glucose

# Step 8: Check the imputed values
summary(heartdata$glucose)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  40.00   72.00   78.79   81.77   85.00  394.00 

Figure B. A moderate relationship between BMI and glucose is visualized in the scatterplot. Since BMI is not normally distributed (QQ Plot), the log of BMI will be used as a predictor for glucose.

Systolic blood pressure is moderately correlated with total cholesterol. Therefore, a model to predict the missing total cholesterol values was created using systolic blood pressure as a predictor.

# Step 1: Set the seed for reproducibility
set.seed(123)

# Step 2: Generate a 10% bootstrap sample from heart_clean
sample_size <- floor(0.1 * nrow(heart_clean))  # 10% of the data
bootstrap_sample <- heart_clean[sample(1:nrow(heart_clean), size = sample_size, replace = TRUE), ]

# Step 3: Create a scatter plot for totChol and sysBP in the bootstrap sample
library(ggplot2)
ggplot(bootstrap_sample, aes(x = totChol, y = sysBP)) +
  geom_point() +
  labs(title = "Scatterplot of Total Cholesterol vs Systolic Blood Pressure (10% Bootstrap Sample)",
       x = "Total Cholesterol",
       y = "Systolic Blood Pressure") +
  theme_minimal()

# Step 1: Build the linear regression model using log(sysBP) to predict totChol
model_totChol <- lm(log(totChol) ~ log(sysBP), data = heart_clean)

# Step 2: Check the summary of the model
summary(model_totChol)

Call:
lm(formula = log(totChol) ~ log(sysBP), data = heart_clean)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.70018 -0.11639  0.00389  0.11928  0.89308 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.14421    0.09172   45.19   <2e-16 ***
log(sysBP)   0.26806    0.01881   14.25   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1795 on 3656 degrees of freedom
Multiple R-squared:  0.05261,   Adjusted R-squared:  0.05235 
F-statistic:   203 on 1 and 3656 DF,  p-value: < 2.2e-16
# Step 3: Identify rows with missing totChol values in heartdata
missing_totChol_rows <- is.na(heartdata$totChol)

# Step 4: Predict log-transformed totChol values for missing rows
predicted_log_totChol <- predict(model_totChol, newdata = heartdata[missing_totChol_rows, ])

# Step 5: Exponentiate the predicted log(totChol) values to get predictions in the original scale
predicted_totChol <- exp(predicted_log_totChol)

# Step 6: Impute the predicted totChol values into the missing positions in heartdata
heartdata$totChol[missing_totChol_rows] <- predicted_totChol

# Step 7: Check the imputed values
summary(heartdata$totChol)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  107.0   206.0   233.6   236.7   262.0   696.0 

Figure C. The scatterplot displays a slight correlation between systolic blood pressure and total cholesterol. Systolic blood pressure is not normally distributed (Figure A)

Fourth, cigarettes per day had 29 missing values. Of the 29 missing values, all individuals were current smokers. The mean number of cigarettes per day was imputed for these missing values.

# Step 1: Calculate the mean of cigsPerDay, excluding missing values (NA)
mean_cigsPerDay <- mean(heartdata$cigsPerDay, na.rm = TRUE)

# Step 2: Impute the missing cigsPerDay values with the calculated mean
heartdata$cigsPerDay[is.na(heartdata$cigsPerDay)] <- mean_cigsPerDay

Last, the data subset still had 1 missing value for heartRate. Since the frequency of this single missing value is very low, the missing value was imputed with the average. The final data set had zero missing values.

# Step 1: Impute missing heartRate values with the average heartRate
mean_heartRate <- mean(heartdata$heartRate, na.rm = TRUE)
heartdata$heartRate[is.na(heartdata$heartRate)] <- mean_heartRate

3 EDA

3.1 Single Variables

All of the variables in this data set were typed as numerical. In turn, indicator variables were changed to factor. For visualization, “male” was changed to “sex”. The indicator values for the categorical variables were changed to descriptive words or phrases to enhance readability of the graphs.

# Rename heartdatato heartviz for visualizing data

heartviz <- heartdata

summary(heartviz)
 male          age        education   currentSmoker   cigsPerDay     BPMeds  
 0:2420   Min.   :32.00   1   :1776   0:2145        Min.   : 0.000   0:4116  
 1:1820   1st Qu.:42.00   2   :1296   1:2095        1st Qu.: 0.000   1: 124  
          Median :49.00   3   : 689                 Median : 0.000           
          Mean   :49.58   4   : 473                 Mean   : 9.006           
          3rd Qu.:56.00   NA's:   6                 3rd Qu.:20.000           
          Max.   :70.00                             Max.   :70.000           
 prevalentStroke prevalentHyp diabetes    totChol          sysBP      
 0:4215          0:2923       0:4131   Min.   :107.0   Min.   : 83.5  
 1:  25          1:1317       1: 109   1st Qu.:206.0   1st Qu.:117.0  
                                       Median :233.6   Median :128.0  
                                       Mean   :236.7   Mean   :132.4  
                                       3rd Qu.:262.0   3rd Qu.:144.0  
                                       Max.   :696.0   Max.   :295.0  
     diaBP            BMI          heartRate         glucose       TenYearCHD
 Min.   : 48.0   Min.   :15.54   Min.   : 44.00   Min.   : 40.00   0:3596    
 1st Qu.: 75.0   1st Qu.:23.08   1st Qu.: 68.00   1st Qu.: 72.00   1: 644    
 Median : 82.0   Median :25.40   Median : 75.00   Median : 78.79             
 Mean   : 82.9   Mean   :25.80   Mean   : 75.88   Mean   : 81.77             
 3rd Qu.: 90.0   3rd Qu.:28.04   3rd Qu.: 83.00   3rd Qu.: 85.00             
 Max.   :142.5   Max.   :56.80   Max.   :143.00   Max.   :394.00             
# Rename the variable 'male' to 'sex'
heartviz <- heartdata %>%
  rename(sex = male)

#colnames(heartviz)

# Check the unique values in the sex column
#unique(heartviz$sex)

# Change values of sex from 1 and 0 to male and female
heartviz$sex <- ifelse(heartviz$sex == 1, "male", 
                            ifelse(heartviz$sex == 0, "female", heartviz$sex))

#glimpse(heartviz)

# Count occurrences of males and females
sex_count <- table(heartviz$sex)

# View the count
#print(sex_count)
# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = sex)) +
   geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "lightblue") + 
  labs(title = "Sex Distribution", x = "Sex", y = "Percent") +
  theme_minimal()

Figure D. The distribution of sex indicates that there are more female data than male data that exists in this data subset. Specifically, there are 2,420 females and 1,820 males included in this data set.

# Recode education variable as a factor with labels
heartviz <- heartviz %>%
  mutate(education = factor(education,
                            levels = c(1, 2, 3, 4),
                            labels = c("Some HS", "HS/GED", "Some College/Vocational School", "College")))

# Filter out any missing values for education
heartviz <- heartviz %>% filter(!is.na(education))

# Calculate the percentage for each education level
education_dist <- heartviz %>%
  group_by(education) %>%
  summarise(count = n()) %>%
  mutate(percentage = (count / sum(count)) * 100)

print(education_dist)
# A tibble: 4 × 3
  education                      count percentage
  <fct>                          <int>      <dbl>
1 Some HS                         1776       41.9
2 HS/GED                          1296       30.6
3 Some College/Vocational School   689       16.3
4 College                          473       11.2
# Create the bar chart using the pre-calculated percentages
ggplot(education_dist, aes(x = education, y = percentage)) +
  geom_bar(stat = "identity", fill = "gold") +
  labs(title = "Distribution of Education Levels", 
       x = "Education Level", 
       y = "Percentage") +
  theme_minimal()

Figure E. The most prevalent education level for the participants in this study is some high school, followed by a high school diploma or GED. The least prevalent education attainment is college degree.

# Change values of currentSmoker
heartviz <- heartviz %>%
  mutate(currentSmoker = ifelse(currentSmoker == 1, "Yes", 
                                 ifelse(currentSmoker == 0, "No", currentSmoker)))

# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = currentSmoker)) +
    geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "orange") + 
  labs(title = "Current Smoker", x = "Smoke", y = "Percent") +
  theme_minimal()

Figure F. The relative frequencies of smokers and non-smokers are almost the same. There were 2,145 participant who were active smokers, which is slightly more than the 2,095 participants who were not active smokers.

# Change values of BPMeds
heartviz <- heartviz %>%
  mutate(BPMeds = ifelse(BPMeds == 1, "Yes", 
                                 ifelse(BPMeds == 0, "No", BPMeds)))

heartviz <- heartviz[!is.na(heartviz$BPMeds), ]  

# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = BPMeds)) +
    geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "darkgreen") + 
  labs(title = "Blood Pressure Medication", x = "BP Medication", y = "Percent") +
  theme_minimal()

Figure G. About 97% of participants were not on blood pressure medication at the time of this study.

heartviz <- heartviz %>%
  mutate(prevalentStroke = ifelse(prevalentStroke == 1, "Yes", 
                                 ifelse(prevalentStroke == 0, "No", prevalentStroke)))
  

# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = prevalentStroke)) +
  geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "violet") + 
  labs(title = "Prevalent Stroke", x = "Prevalent Stroke", y = "Percent") +
  theme_minimal()

Figure H. Very few participants, less than 0.6%, had a stroke.

heartviz <- heartviz %>%
  mutate(prevalentHyp = ifelse(prevalentHyp == 1, "Yes", 
                                 ifelse(prevalentHyp == 0, "No", prevalentHyp)))
  

# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = prevalentHyp)) +
  geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "royalblue") + 
  labs(title = "Prevalent Hypertensive", x = "Prevalent Hypertensive", y = "Percent") +
  theme_minimal()

Figure I. About 31% of participants had received treatment for hypertension.

heartviz <- heartviz %>%
  mutate(diabetes = ifelse(diabetes == 1, "Yes", 
                                 ifelse(diabetes == 0, "No", diabetes)))
  

# Create a bar chart using ggplot2
ggplot(heartviz, aes(x = diabetes)) +
  geom_bar(aes(y = (after_stat(count) / sum(after_stat(count))) * 100), fill = "brown") + 
  labs(title = "Diabetes", x = "Diabetes", y = "Percent") +
  theme_minimal()

Figure J. Diabetes was infrequent, about 2.6%, among the participants of this study.

#Histogram for age
ggplot(heartviz, aes(x = age)) + 
  geom_histogram(aes(y = ..density..), binwidth = 1, fill = "blue", color = "black", alpha = 0.5) + 
  geom_density(color = "red", size = 1) + 
  labs(title = "Histogram of Age", 
       x = "Age", 
       y = "Density") +
  theme_minimal()

Figure K. The age distribution varies from slightly over 30 years of age to about 70 years old. A slight skewness to the right is apparent. The typical age of a participant is about 50 years old.

# Create a copy of the heartdata data set
heartdataCHD <- heartdata
# Recode TenYearCHD in heartdataCHD
heartdataCHD$TenYearCHD <- ifelse(heartdataCHD$TenYearCHD == 1, "Yes", "No")

# Create the bar chart
ggplot(heartdataCHD, aes(x = TenYearCHD)) +
  geom_bar(aes(y = (..count..) / sum(..count..)), fill = "forestgreen", color = "black") +
  scale_y_continuous(labels = function(x) paste0(round(x * 100, 1), "%")) +
  labs(title = "Ten-Year Coronary Heart Disease (CHD) Outcomes",
       x = "Ten-Year CHD", y = "Percentage") +
  theme_minimal()

Figure L. Less than 20% of participants developed CHD within ten years of their initial examination.

# Create a density plot for cigsPerDay using ggplot2
ggplot(data = heartdata, aes(x = cigsPerDay)) + 
  geom_density(na.rm = TRUE, fill = "blue", alpha = 0.5) +  # Fill color and transparency
  labs(title = "Density Curve for Cigarettes Per Day", 
       x = "Cigarettes Per Day", 
       y = "Density")

Figure M. The density curve for the number of cigarettes per day is multi-modal. The distribution has an overall skewed right shape. The mean number of cigarettes per day is nine, which is greater than the median number of cigarettes per day of zero. The number of cigarettes per day varies from 0 to 70 and has an IQR of 20.

# Create a density plot for totChol using ggplot2
ggplot(data = heartdata, aes(x = totChol)) + 
  geom_density(na.rm = TRUE, fill = "purple", alpha = 0.5) +  # Fill color and transparency
  labs(title = "1. Density Curve for Total Cholesterol", 
       x = "Cholesterol", 
       y = "Density")

# Calculate the mean and standard deviation of totChol
mean_totChol <- mean(heartdata$totChol, na.rm = TRUE)
sd_totChol <- sd(heartdata$totChol, na.rm = TRUE)

# Create a density plot for totChol and overlay a normal curve
ggplot(data = heartdata, aes(x = totChol)) + 
  geom_density(na.rm = TRUE, fill = "purple", alpha = 0.5) +  # Density curve
  stat_function(fun = dnorm, args = list(mean = mean_totChol, sd = sd_totChol), 
                color = "red", linetype = "dashed", size = 1) +  # Normal curve
  labs(title = "2. Density Curve for Total Cholesterol with Normal Curve", 
       x = "Cholesterol", 
       y = "Density") +
  theme_minimal()

# Perform Shapiro-Wilk test for normality
shapiro_test_result <- shapiro.test(heartdata$totChol)

# Print the test result
shapiro_test_result

    Shapiro-Wilk normality test

data:  heartdata$totChol
W = 0.96831, p-value < 2.2e-16
# Create a box plot for the totChol variable
ggplot(data = heartdata, aes(x = "", y = totChol)) +
  geom_boxplot(outlier.colour = "red", outlier.shape = 16, outlier.size = 2) +  # Box plot with custom outliers
  labs(title = "3. Box Plot of Total Cholesterol", 
       x = "", 
       y = "Cholesterol") +
  theme_minimal()

# Calculate the standard deviation of totChol, ignoring missing values
sd_totChol <- sd(heartdata$totChol, na.rm = TRUE)

# Print the standard deviation
sd_totChol
[1] 44.34193
# Fit a linear model
model <- lm(totChol ~ age + BMI + heartRate, data = heartdata)

# Calculate the Jackknife residuals
jackknife_residuals <- rstudent(model)  # Studentized residuals are used to identify outliers

# Add Jackknife residuals to the data set
heartdata$jackknife_residuals <- jackknife_residuals

# Identify potential outliers where the absolute value of Jackknife residuals > 2
outliers <- heartdata[abs(heartdata$jackknife_residuals) > 2, ]

# Display potential outliers
#outliers

Figure N.

  1. The total cholesterol distribution appears to be roughly symmetric with extreme low and high values. The mean total cholesterol is 236.7 mg/dL and standard deviation of 44.3 mg/dL. The median total cholesterol is 234 mg/dL, which is slightly less than the mean indicating a slightly skewed right distribution. The data varies from 107 mg/dL to 696 mg/dL.

  2. Although the distribution has a slight skewness to the right, the distribution appears to fit the Normal curve moderately well. However, the Shapiro-Wilk test resulted in a p-value of < 2.2e-16, which rejects the null hypothesis, indicating that there is not convincing evidence of a Normal distribution for total cholesterol.

  3. An examination of a box plot indicates low and high outliers. The results of a jackknife residual analysis indicates 173 outliers. The majority of outliers are high outliers.

# Create a density plot for sysBP using ggplot2
ggplot(data = heartdata, aes(x = sysBP)) + 
  geom_density(na.rm = TRUE, fill = "gold", alpha = 0.5) +  # Fill color and transparency
  labs(title = "Density Curve for Systolic Blood Pressure", 
       x = "Systolic BP", 
       y = "Density")

# Create a box plot for sysBP
ggplot(data = heartdata, aes(x = "", y = sysBP)) + 
  geom_boxplot(fill = "white", outlier.colour = "red", outlier.shape = 16, outlier.size = 2) +
  labs(title = "Box Plot of Systolic Blood Pressure (sysBP)", 
       y = "Systolic Blood Pressure (sysBP)") +
  theme_minimal()

Figure O. Systolic Blood Pressure distribution is skewed right. The median is 128 mmHg with an IQR of 27 mmHg. Several high outliers exist in this distribution. The highest systolic blood pressure is 295 mmHg.

# Create a density plot for diaBP using ggplot2
ggplot(data = heartdata, aes(x = diaBP)) + 
  geom_density(na.rm = TRUE, fill = "yellow", alpha = 0.5) +  # Fill color and transparency
  labs(title = "1. Density Curve for Diastolic Blood Pressure", 
       x = "Diastolic BP", 
       y = "Density")

# Calculate mean and standard deviation for normal curve
mean_diaBP <- mean(heartdata$diaBP, na.rm = TRUE)
sd_diaBP <- sd(heartdata$diaBP, na.rm = TRUE)


# Create histogram with normal curve overlay
ggplot(data = heartdata, aes(x = diaBP)) + 
  geom_histogram(aes(y = ..density..), bins = 30, fill = "lightblue", color = "black", alpha = 0.7) + 
  stat_function(fun = dnorm, args = list(mean = mean_diaBP, sd = sd_diaBP), color = "red", size = 1) +
  labs(title = "2. Histogram of Diastolic Blood Pressure (diaBP) with Normal Curve", 
       x = "Diastolic Blood Pressure (diaBP)", 
       y = "Density") +
  theme_minimal()

# Create a box plot for diaBP
ggplot(data = heartdata, aes(x = "", y = diaBP)) + 
  geom_boxplot(fill = "lightblue", outlier.colour = "red", outlier.shape = 16, outlier.size = 2) +
  labs(title = "3. Box Plot of Diastolic Blood Pressure (diaBP)", 
       y = "Diastolic Blood Pressure (diaBP)") +
  theme_minimal()

# Perform Shapiro-Wilk normality test for diaBP
shapiro_test_result <- shapiro.test(heartdata$diaBP)

# Print the results
#print(shapiro_test_result)

# Create a Q-Q plot for diaBP
qqnorm(heartdata$diaBP, main = "4. Q-Q Plot of Diastolic Blood Pressure (diaBP)")
qqline(heartdata$diaBP, col = "red")  # Add a reference line

Figure P.

  1. The density curve for the diastolic BP distribution has a roughly symmetric. The mean of 82.9 mmHg is similar to the median of 82 mmHg, confirming a roughly symmetric distribution. The diastolic BP data varies from 48 to 142.5 mmHg.

  2. A Normal curve superimposed over a histogram indicates that the Diastolic BP distribution may be Normally distributed. However, a Shapiro-Wilk normality test indicates that the distribution is not normal.

  3. Both low and high outliers can be seen in the box plot. Most of the observations that are outliers are high.

  4. The Q-Q plot has a concave up form which confirms that the Diastolic BP distribution is not normal.

# Create a density plot for BMI using ggplot2
ggplot(data = heartdata, aes(x = BMI)) + 
  geom_density(na.rm = TRUE, fill = "purple", alpha = 0.5) +  # Fill color and transparency
  labs(title = "Density Curve for BMI", 
       x = "BMI", 
       y = "Density")

# Calculate the mean and standard deviation of BMI
mean_BMI <- mean(heartdata$BMI, na.rm = TRUE)
sd_BMI <- sd(heartdata$BMI, na.rm = TRUE)

# Create a histogram with a normal curve superimposed
ggplot(heartdata, aes(x = BMI)) +
  geom_histogram(aes(y = ..density..), binwidth = 1, fill = "lightblue", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean_BMI, sd = sd_BMI), color = "red", size = 1) +
  labs(title = "Histogram of BMI with Normal Curve", x = "BMI", y = "Density") +
  theme_minimal()

# Create a box plot for BMI
ggplot(heartdata, aes(y = BMI)) +
  geom_boxplot(fill = "lightgreen", color = "black") +
  labs(title = "Box Plot of BMI", y = "BMI") +
  theme_minimal()

# Calculate the standard deviation of BMI
sd_BMI <- sd(heartdata$BMI, na.rm = TRUE)

# Display the result
sd_BMI
[1] 4.072664
# Perform Shapiro-Wilk normality test on BMI
shapiro_test_BMI <- shapiro.test(heartdata$BMI)

# View the result
#shapiro_test_BMI

# Create a Q-Q plot for BMI
qqnorm(heartdata$BMI, main = "Q-Q Plot of BMI")
qqline(heartdata$BMI, col = "red")  # Add a reference line

Figure Q. The BMI distribution appears to have a slightly skewed right distribution due to some extremely high values. Without these outliers, the data appears roughly symmetric. The data varies from 15.54 to 56.80 kg/m^2. The mean BMI for this data subset is 25.8 kg/m^2, which is similar to the median value of 25.4 kg/m^2. The standard deviation is 4.07 kg/m^2.

# Create a density plot for heartRate using ggplot2
ggplot(data = heartdata, aes(x = heartRate)) + 
  geom_density(na.rm = TRUE, fill = "lightblue", alpha = 0.5) +  # Fill color and transparency
  labs(title = "Density Curve for Heart Rate", 
       x = "Heart Rate", 
       y = "Density")

# Calculate the mean and standard deviation of heartRate
mean_heartRate <- mean(heartdata$heartRate, na.rm = TRUE)
sd_heartRate <- sd(heartdata$heartRate, na.rm = TRUE)

# Create a histogram for heartRate with a normal curve superimposed
ggplot(heartdata, aes(x = heartRate)) +
  geom_histogram(aes(y = ..density..), binwidth = 1, fill = "seashell", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean_heartRate, sd = sd_heartRate), color = "red", size = 1) +
  labs(title = "Histogram of Heart Rate with Normal Curve", x = "Heart Rate", y = "Density") +
  theme_minimal()

# Create a box plot for heartRate
ggplot(heartdata, aes(y = heartRate)) +
  geom_boxplot(fill = "lavender", color = "black") +
  labs(title = "Box Plot of Heart Rate", y = "Heart Rate") +
  theme_minimal()

# Calculate the standard deviation of heartRate
sd_heartRate <- sd(heartdata$heartRate, na.rm = TRUE)

# Display the result
sd_heartRate
[1] 12.02393
# Perform Shapiro-Wilk normality test for heartRate
shapiro_test_result <- shapiro.test(heartdata$heartRate)

# Display the result
#shapiro_test_result

# Create a Q-Q plot for heartRate
qqnorm(heartdata$heartRate, main = "Q-Q Plot of Heart Rate")
qqline(heartdata$heartRate, col = "red")  # Add a reference line

Figure R. The heartRate distribution appears to be roughly symmetric with the exception of a few high outliers. The mean heart rate is 75.88 beats/min which is close to the median heart rate of 75 beats/min. The standard deviation is 12.02 beats/min. The Shapiro Wilk test for normality produces a test statistic very close to 1, indicating normality. However, the test was insignificant. The QQ plot possibly indicates that the distribution follows a t-distribution.

# Create a density plot for glucose using ggplot2
ggplot(data = heartdata, aes(x = glucose)) + 
  geom_density(na.rm = TRUE, fill = "lavender", alpha = 0.5) +  # Fill color and transparency
  labs(title = "Density Curve for Glucose", 
       x = "Glucose", 
       y = "Density")

# Create a box plot for glucose
ggplot(data = heartdata, aes(y = glucose)) + 
  geom_boxplot(fill = "lightblue", outlier.colour = "red", outlier.size = 2) + 
  labs(title = "Box Plot of Glucose Levels", 
       y = "Glucose Level") + 
  theme_minimal()

# Calculate the standard deviation of glucose
#std_dev_glucose <- sd(heartdata$glucose, na.rm = TRUE)  # na.rm = TRUE excludes any missing values
#std_dev_glucose

Figure S. The glucose distribution is skewed right. The median glucose level is 79 mg/dL which is slightly less than the mean glucose level of 81.93 mg/dL. The standard deviation of glucose is 22.85 mg/dL. The glucose levels vary from 40 to 394 mg/dL. The majority of extreme values are high outliers and about three values are low outliers.

3.2 Pairwise Relationships

The pairwise relationships between variables are examined. The variables considered are numerical.

# Step 1: Define the original quantitative variables
quantitative_vars <- heartdata[, c("age", "cigsPerDay", "totChol", "sysBP", "diaBP", "BMI", "heartRate", "glucose")]

# Step 2: Compute the correlation matrix for the specified quantitative variables
correlation_matrix <- cor(quantitative_vars, use = "pairwise.complete.obs")

# Step 3: Visualize the correlation matrix using corrplot
# Adjust margins to prevent squishing and cut-off title
par(mar = c(5, 1, 4, 1))  # bottom, left, top, right margins

# Increase the size of the plot by adjusting the layout parameters
corrplot(correlation_matrix, 
         method = "circle",        # Choose 'circle' for circular visualization
         type = "lower",           # Show only the lower triangle of the matrix
         addCoef.col = "black",    # Add correlation coefficients in black
         title = "Correlation Matrix for Original Quantitative Variables",
         tl.col = "black",         # Color for the text labels
         tl.srt = 45,              # Rotate text labels for better readability
         cl.lim = c(-1, 1),        # Set color limits
         number.cex = 0.7,         # Adjust font size of the numbers
         tl.cex = 0.8,             # Adjust font size of the text labels
         cex.main = 1.2)           # Adjust title size

Figure T. The strongest correlation is between systolic and diastolic blood pressure. Age and Systolic BP have a strong positive correlation of 0.39. As age increases, so does Systolic BP. BMI is also positively related to Systolic and Diastolic BP. As BMI increases, so does both of the BP measures. Total cholesterol and age have a positive correlation of 0.26. Most other pairwise variables are positively correlated with correlation coefficients of less than 0.20. There are just a few pairwise variables that have negative and weak correlations, and most of the variables are negatively correlated with cigsPerDay.

# Step 1: Filter for numeric variables, excluding specific columns, those with "log", "predicted"
numeric_vars <- heartdata %>%
  select(where(is.numeric), -c(jackknife_residuals, currentSmoker)) %>%
  select(-contains("log"), -contains("predicted"))

# Step 2: Calculate the correlation matrix
cor_matrix <- cor(numeric_vars, use = "complete.obs")

# Get the absolute correlation values and convert to a data frame
cor_df <- as.data.frame(as.table(cor_matrix))
cor_df <- cor_df[order(-abs(cor_df$Freq)), ]  # Sort by absolute correlation

# Step 3: Get the top 6 unique correlated variable pairs, excluding reverse duplicates
top_cor_vars <- cor_df %>%
  filter(Var1 != Var2) %>%
  filter(!duplicated(t(apply(.[, 1:2], 1, sort)))) %>%  # Remove reverse duplicates
  distinct(Var1, Var2) %>%
  head(6)

# Step 4: Subset the original numeric data for the top correlated variables
subset_vars <- unique(c(top_cor_vars$Var1, top_cor_vars$Var2))
subset_data <- numeric_vars %>%
  select(all_of(subset_vars))

# Step 5: Create scatterplots for each unique pair
for (i in 1:nrow(top_cor_vars)) {
  var1_name <- top_cor_vars$Var1[i]
  var2_name <- top_cor_vars$Var2[i]
  
  # Create the scatterplot
  print(
    xyplot(as.formula(paste(var1_name, "~", var2_name)), data = subset_data,
           main = paste("Scatterplot of", var1_name, "vs", var2_name),
           xlab = var2_name,
           ylab = var1_name,
           panel = function(...) {
             panel.grid(h = -1, v = -1)  # Add grid
             panel.xyplot(...)  # Scatter points
           })
  )
}

Figure U. Scatterplots for top 6 most correlated pairs of original variables. Diastolic & systolic BP are highly linearly correlated, as clinically expected. All other pairs (Systolic BP & age, BMI & Diastolic/Systolic BP, Total Cholesterol & age, Total Cholesterol & Systolic BP) are positively correlated.

#scatterplot age & cigsPerDay
ggplot(heartdata, aes(x = age, y = cigsPerDay)) +
  geom_point(color = "blue") +
  geom_smooth(method = "lm", color = "red", se = FALSE) +
  labs(title = "Age vs Cigarettes Per Day with Trend Line",
       x = "Age",
       y = "Cigarettes Per Day") +
  theme_minimal()

Figure V. Age & CigsPerDay is the most strongly and negatively correlated variables. As participants increase in age, the number of cigarettes per day decreases.

4 FEATURE ENGINEER

According to the CDC, smoking increases the risk of cardiovascular disease. (https://www.cdc.gov/tobacco/about/cigarettes-and-cardiovascular-disease.html). Cardiovascular disease includes heart disease, stroke, and peripheral artery disease. CHD, coronary heart disease, is the most common types of heart disease in the US.

Cigarettes per day, age, and total cholesterol are all variables in the Framingham Heart Data subset that are positively correlated with CHD. To capture the combined effects of these factors, a new variable named “smoking_risk_index” was created. This variable equally weights cigsPerDay, age, and totChol. The values of smoking_risk_index ranks individuals based on their smoking behavior, age, and cholesterol levels. The higher the index value, the higher an individual is at risk for CHD.

#create smoking risk index using weighting

heartdata$smoking_risk_index <- (heartdata$cigsPerDay * 0.333) + 
                                 (heartdata$age * 0.333) + 
                                 (heartdata$totChol* 0.333)
#head(heartdata)


ggplot(heartdata, aes(x = smoking_risk_index)) +
  geom_histogram(binwidth = 10, fill = "blue", color = "black") +
  labs(title = "Distribution of Smoking Risk Index", 
       x = "Smoking Risk Index", 
       y = "Count") +
  theme_minimal()

# Generate QQ plot for smoking_risk_index
qqnorm(heartdata$smoking_risk_index, main = "QQ Plot of Smoking Risk Index")
qqline(heartdata$smoking_risk_index, col = "red")

#summary statistics
summary(heartdata$smoking_risk_index)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  56.28   87.58   97.57   98.32  108.56  251.75 
# Standard deviation
#sd(heartdata$smoking_risk_index)

Figure W. The smoking risk index distribution is roughly symmetric with a mean index of 98.35 and standard deviation of 16.02. The values of smoking_risk_index varies from 56.28 to 251.75. About 25% of participants have smoking_risk_index at or below 87.58 and about 25% of participants have smoking risk index values at or greater than 108.56. The QQ plot indicates that the distribution of smoking_risk_index is approximately normal.

# Set seed for reproducibility
set.seed(123)

# Step 1: Create a 5% bootstrap sample (sampling with replacement)
n <- nrow(heartdata)
bootstrap_sample <- heartdata[sample(1:n, size = 0.1 * n, replace = TRUE), ]

# Step 2: Reshape the data to long format for ggplot
long_data <- bootstrap_sample %>%
  select(smoking_risk_index, sysBP, diaBP, BMI, heartRate, glucose) %>%
  pivot_longer(-smoking_risk_index, names_to = "variable", values_to = "value")

# Step 3: Create scatterplots with facets
ggplot(long_data, aes(x = smoking_risk_index, y = value)) +
  geom_point(alpha = 0.5) +
  facet_wrap(~ variable, scales = "free_y") +
  labs(title = "Relationship between Smoking Risk Index and Other Variables (Bootstrap 5% Sample)",
       x = "Smoking Risk Index",
       y = "Value") +
  theme_minimal()

Figure X. The scatterplots visualize the relationship between the feature engineer, smoking_risk_index, against original numeric variables that are not factors. The relationships are weak with BMI, heartRate, and systolic BP variables. The relationship with glucose is not remarkable, either.

A slight linear relationship can be seen with diastolic BP.

n <- nrow(heartdata)
bootstrap_sample2 <- heartdata[sample(1:n, size = 0.2 * n, replace = TRUE), ]

ggplot(data = bootstrap_sample2, aes(x = smoking_risk_index, y = diaBP)) +
  geom_point(color = "blue", alpha = 0.6) +
  ggtitle("Scatterplot of Diastolic BP and Smoking Risk Index") +
  xlab("Smoking Risk Index") +
  ylab("Diastolic BP") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

Figure Y. A linear relationship between smoking risk index and diastolic BP can be see in the scatterplot. The relationship is likely due to the strong correlation between diastolic BP and systolic BP, and systolic BP is a factor in the smoking risk index variable.

5 ASSOCIATION ANALYSIS I

This analysis examines smoking and sex for participants. How does smoking impact metrics in the Framingham Heart Study? Is sex a significant factor in some metrics?

5.1 Smoker Analysis

To analyze the cigsPerDay, a new variable named “smoking_category” is created. Participants who smoke 20 or more cigarettes per day are labelled “heavy”. Participants who smoke less than 20 cigarettes per day are labelled “moderate”. All non-smokers have been excluded from this analysis. This variable facilitates comparisons between groups.

# Load the required libraries
library(dplyr)

# Create heartdata3 from heartdata
heartdata3 <- heartdata %>%
  mutate(smoking_category = ifelse(cigsPerDay >= 20, "heavy", "moderate")) %>%
  filter(currentSmoker == 1)
# Step 1: Create the heartdata3 dataset
heartdata3 <- heartdata[heartdata$currentSmoker == 1, ]  # Filter only current smokers
heartdata3$smoking_category <- ifelse(heartdata3$cigsPerDay >= 20, "heavy", "moderate")  # Create smoking_category

# Step 2: List of variables to compare with smoking_category
variables <- c("prevalentStroke", "prevalentHyp", "diabetes", "male", "BPMeds", "TenYearCHD")

# Create a list to store the plots
plot_list <- list()

# Step 3: Loop through each variable to create mosaic plots
for (var in variables) {
  p <- ggplot(data = heartdata3) +
    geom_mosaic(aes(x = product(smoking_category, !!sym(var)), fill = smoking_category)) +
    labs(title = paste("Smoking Ctgry vs", var),
         x = var,
         y = "Smoking Ctgry") +
    scale_fill_manual(values = c("heavy" = "blue", "moderate" = "red")) +  # Customize colors as needed
    theme_minimal() +
    theme(plot.title = element_text(hjust = 0.5, size = 10, face = "bold"),  # Smaller title size
          axis.title.y = element_text(angle = 0, vjust = 0.5, size = 10),  # Adjust y-axis title size
          axis.title.x = element_text(size = 10),  # Adjust x-axis title size
          legend.position = "none")  # Remove legend
  plot_list[[var]] <- p
}

# Step 4: Arrange the plots in a grid with controlled size
gridExtra::grid.arrange(grobs = plot_list, ncol = 2, 
             top = grid::textGrob("Comparison of Smoking Category with Various Health Outcomes", gp = grid::gpar(fontsize = 12, fontface = "bold")),  # Smaller top title
             padding = unit(2, "lines"))

Figure Z. Heavy smokers in this study are more frequently male, have lower incidents of stroke, and have slightly higher incidence of 10 year risk of coronary heart disease. No association with smoking category can be visualized in the mosaic plots between prevalent hypertensive and diabetes.

Systolic BP is not normally distributed. Therefore, a log transformation is done to conduct an ANOVA analysis with smoking status (currentSmoker). Levene’s Test indicates that the variances are unequal. Therefore, Welch’s ANOVA is conducted. Based on the available data, there is evidence of a significant difference in systolic BP between smokers and non-smokers.

# Create a boxplot of sysBP by currentSmoker
ggplot(heartdata, aes(x = factor(currentSmoker), y = sysBP)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Comparison of Systolic Blood Pressure (sysBP) by Smoking Status",
       x = "Current Smoker (0 = No, 1 = Yes)",
       y = "Systolic Blood Pressure (sysBP)") +
  theme_minimal()

# Apply log transformation to sysBP
heartdata$log_sysBP <- log(heartdata$sysBP)


# Perform Welch's ANOVA with transformed sysBP
welch_anova_result <- oneway.test(log_sysBP ~ currentSmoker, data = heartdata, var.equal = FALSE)

# Summary of Welch's ANOVA result
welch_anova_result

    One-way analysis of means (not assuming equal variances)

data:  log_sysBP and currentSmoker
F = 74.968, num df = 1.0, denom df = 4227.2, p-value < 2.2e-16

Figure AA. Welch’s ANOVA results are significant. Based on the available data, there is evidence of a significant difference in systolic BP between smokers and non-smokers. In fact, current smokers have lower systolic blood pressure than non-smokers.

Diastolic BP is not normally distributed. Therefore, a log transformation is done to conduct an ANOVA analysis with smoking status (currentSmoker). Based on Levene’s Test, the variances are the same. In turn, assumptions are met. Based on the available data, there is evidence of a significant difference in diastolic BP between smokers and non-smokers.

# Create a boxplot of diaBP by currentSmoker
ggplot(heartdata, aes(x = factor(currentSmoker), y = diaBP)) +
  geom_boxplot(fill = "green") +
  labs(title = "Comparison of Diastolic Blood Pressure (sysBP) by Smoking Status",
       x = "Current Smoker (0 = No, 1 = Yes)",
       y = "Diastolic Blood Pressure (diaBP)") +
  theme_minimal()

# Apply log transformation to sysBP
heartdata$log_diaBP <- log(heartdata$diaBP)

# Perform Levene's Test for homogeneity of variances
leveneTest(log_diaBP ~ currentSmoker, data = heartdata)
Levene's Test for Homogeneity of Variance (center = median)
        Df F value Pr(>F)
group    1   0.714 0.3982
      4238               
# Perform ANOVA with transformed sysBP
anova_result2 <- aov(log_diaBP ~ currentSmoker, data = heartdata)

# Summary of ANOVA result
summary(anova_result2)
                Df Sum Sq Mean Sq F value   Pr(>F)    
currentSmoker    1   1.03  1.0332   52.79 4.41e-13 ***
Residuals     4238  82.95  0.0196                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Figure BB. The ANOVA analysis was significant. Based on the available data, there is evidence of a significant difference in diastolic BP between smokers and non-smokers. Similar to findings for systolic blood pressure, smokers have lower diastolic blood pressure compared to non-smokers.

# Create the new variables age_group and glucose_level
heartdata <- heartdata %>%
  mutate(
    # Create age_group based on age
    age_group = case_when(
      age <= 59 ~ "middle-aged",
      age >= 60 ~ "older"
    )
  )

# Boxplot for age_group and cigsPerDay
ggplot(heartdata, aes(x = age_group, y = cigsPerDay)) +
  geom_boxplot(fill = "blue") +
  labs(title = "Cigarettes per Day by Age Group",
       x = "Age Group", 
       y = "Cigarettes per Day") +
  theme_minimal()

Figure CC. Middle-aged adults are 59 years old or young. Older adults are 60 or older. Middle aged adults who smoke in the Framingham Heart Study tend to smoke more cigarettes per day than older adults who smoke in the Framingham Heart Study.

ggplot(data = heartdata, aes(x = sysBP, fill = TenYearCHD)) +
  geom_density(alpha = 0.3) +
  ggtitle("Ten Year CHD vs Systolic BP") + 
  theme(plot.title = element_text(hjust = 0.5),
        legend.position="top") +
  xlab("Systolic BP") + 
  ylab("Ten Year CHD")

Figure DD. Participants who were at risk for 10 year CHD have higher systolic BP than for participants who were not at risk for 10 year CHD.

heartdata$prevalentHyp <- as.character(heartdata$prevalentHyp)
heartdata$currentSmoker <- as.character(heartdata$currentSmoker)
heartdata$BPMeds <- as.character(heartdata$BPMeds)
heartdata$male <- as.character(heartdata$male)
heartdata$education <- as.character(heartdata$education)
heartdata$prevalentStroke <- as.character(heartdata$prevalentStroke)
heartdata$diabetes <- as.character(heartdata$diabetes)

#str(heartdata)

ggplot(data = heartdata, aes(x = diaBP, fill = prevalentHyp)) +
  geom_density(alpha = 0.3) +
  ggtitle("Prevalent Hypertensive vs Diastolic BP") + 
  theme(plot.title = element_text(hjust = 0.5),
        legend.position="top") +
  xlab("Diastolic BP") + 
  ylab("Prevalent Hypertensive")

Figure EE. For prevalent hypertensive participants, the typical diastolic blood pressure is higher compared to those who are not prevalent hypertensive, as seen in the graph. In turn, there is an association between prevalent hypertensive and diastolic blood pressure.

5.2 Sex Analysis

Sex analysis may be informative in the context of health due to inherent biological differences. The differences in ten year risk for CHD between male and female is significantly different. In fact, male in this study have a higher ten year risk for CHD than female.

# Load necessary libraries
library(ggmosaic)

# Create the mosaic plot
mosaicplot(~ TenYearCHD + sex, 
           data = heartviz, 
           color = c("blue", "yellow"), 
           main = "Mosaic Plot of TenYearCHD and Sex",
           xlab = "TenYearCHD", 
           ylab = "Sex")

Figure FF. Male participants in the study have a higher predicted ten year CHD risk compared to female participants in the study.

To further investigate the association visualized in the mosaic plot, a Chi Square Test of General Association is conducted (Table 2). All expected cell counts are equal to or greater than 5. Therefore, the test is justified.

Ho: There is no association between sex and TenYearCHD. Ha: There is an association between sex and TenYearCHD

There is evidence to suggest that an association exists between sex and TenYearCHD.

# Step 1: Create a contingency table
contingency_table <- table(heartdata$male, heartdata$TenYearCHD)

# Step 2: Display the contingency table
print(contingency_table)
   
       0    1
  0 2119  301
  1 1477  343
# Step 3: Perform the Chi-square test
chi_square_result <- chisq.test(contingency_table)

# Step 4: Display the results
print(chi_square_result)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 32.618, df = 1, p-value = 1.122e-08

Table 2. A general test of association between sex and TenYearCHD results in significant differences between male and female participants. In fact, male participants have a higher risk of developing CHD in the next 10 years.

6 ASSOCIATION ANALYSIS II

6.1 Diabetes

The correlation between diabetes and glucose is 0.605708655, which indicates a strong relationship.The association of some variables, such as age, glucose levels, and BMI with diabetes is also examined. Three new variables are created to facilitate this analysis.

According to NIH (https://pmc.ncbi.nlm.nih.gov/articles/PMC10425921/), “young” means less than 35 years old, “middle-aged” means 36-59, and “older” means 60+ yrs old. Since the minimum age of participants in this data subset is 32, the “young” category is excluded in the analysis. A variable called age_group is created in the data set according to these guidelines.

Many tests are used to diagnose diabetes including glucose levels. According to the CDC (https://www.cdc.gov/diabetes/diabetes-testing/index.html), glucose levels greater than 126 mg/dL indicates diabetes, levels between 100 and 125 mg/dL indicates prediabetes, and normal levels is measured at 99 mg/dL or less. A new variable “glucose_level” is created based these CDC guidelines.

BMI is a screening measure that is used with other measures to assess health. A variable “BMI_category” has been created using the BMI data. According to the CDC (https://www.cdc.gov/bmi/adult-calculator/bmi-categories.html), adults 20 years or older who have BMI less than 18.5 are underweight, 18.5 to less than 25 are healthy weight, 25 to less than 30 are overweight, and 30 or greater are obese.

# Create the new variables age_group and glucose_level
heartdata <- heartdata %>%
  mutate(
    # Create age_group based on age
    age_group = case_when(
      age <= 59 ~ "middle-aged",
      age >= 60 ~ "older"
    ),
    
    # Create glucose_level based on glucose
    glucose_level = case_when(
      glucose <= 99 ~ "normal",
      glucose >= 100 & glucose <= 125 ~ "prediabetes",
      glucose >= 126 ~ "diabetes"
    )
  )

# Create the BMI_category variable
heartdata <- heartdata %>%
  mutate(
    BMI_category = case_when(
      BMI < 18.5 ~ "underweight",
      BMI >= 18.5 & BMI < 25 ~ "healthy_weight",
      BMI >= 25 & BMI < 30 ~ "overweight",
      BMI >= 30 ~ "obese"
    )
  )


# Mosaic plot comparing age_group to diabetes
mosaicplot(table(heartdata$age_group, heartdata$diabetes), 
           main = "Mosaic Plot: Age Group vs. Diabetes", 
           xlab = "Age Group", 
           ylab = "Diabetes Status", 
           col = c("lightblue", "lightcoral"))

# Mosaic plot comparing glucose_level to diabetes
mosaicplot(table(heartdata$glucose_level, heartdata$diabetes), 
           main = "Mosaic Plot: Glucose Level vs. Diabetes", 
           xlab = "Glucose Level", 
           ylab = "Diabetes Status", 
           col = c("lightgreen", "lightpink"))

# Mosaic plot comparing age_group to glucose_level
mosaicplot(table(heartdata$age_group, heartdata$glucose_level), 
           main = "Mosaic Plot: Age Group vs. Glucose Level", 
           xlab = "Age Group", 
           ylab = "Glucose Level", 
           col = c("lightyellow", "lightblue"))

# Create a mosaic plot for BMI_category and glucose_level
mosaicplot(table(heartdata$BMI_category, heartdata$glucose_level),
           main = "Mosaic Plot: BMI Category vs. Glucose Level",
           xlab = "BMI Category",
           ylab = "Glucose Level",
           col = c("lightblue", "lightgreen", "lightpink"),
           border = "gray")

# Create a mosaic plot for BMI_category and diabetes
mosaicplot(table(heartdata$BMI_category, heartdata$diabetes),
           main = "Mosaic Plot: BMI Category vs. diabetes",
           xlab = "BMI Category",
           ylab = "Diabetes Status",
           col = c("lightblue", "lightgreen", "lightpink"),
           border = "gray")

Figure GG. The Age Group vs. Diabetes mosaic plot displays an association between age group and diabetes. Older people tend to have a higher frequency of diabetes than middle-aged adults. The glucose level that indicates diabetes has the highest incidence of diabetes in the Glucose Level vs. Diabetes mosaic plot. The prediabetes glucose group has a higher frequency of diabetes than the normal glucose level group. An association can be seen between age group and glucose level in the Age Group vs. Glucose Level mosaic plot. Older adults tend to have higher glucose levels which indicate more prediabetes and diabetes compared to the other two younger age groups. A possible association can be seen between glucose level and BMI category, especially between underweight and healthy weight vs overweight and obese in the BMI Category vs Glucose Level mosaic plot. In the BMI Category vs Diabetes mosaic plot, a relationship can be seen between healthy weight and obese.

In turn, two way tables are created and appropriate association tests conducted.

6.2 Glucose vs Diabetes, Controlling for Age

Analysis of the correlation between glucose level and diabetes, while controlling for age, was conducted with three degrees of freedom (Table 3). The contingency tables are 3X2 ordered-nominal arrays. All cell counts are greater than or equal to 5 and all row totals are greater than or equal to 20 when combined over all tables. Therefore, the test is justified.

Ho: There is no association between glucose level and diabetes status when controlling for age. Ha: There is a correlation between glucose level and diabetes status when controlling for age.

Since the p-value is approximately zero, which is less than 0.05, we reject the null. There is strong significant evidence of a positive correlation between glucose level and diabetes when controlling for age group. As glucose level increases, the diabetes status increases.

# Ensure the factors are ordered
heartdata <- heartdata %>%
  mutate(
    age_group = factor(age_group, levels = c("middle-aged", "older")),
    glucose_level = factor(glucose_level, levels = c("normal", "prediabetes", "diabetes")),
    BMI_category = factor(BMI_category, levels = c("underweight", "healthy_weight", "overweight", "obese"))
  )

# Convert glucose_level to an ordered factor
heartdata$glucose_level <- factor(heartdata$glucose_level, 
                                   levels = c("normal", "prediabetes", "diabetes"),
                                   ordered = TRUE)

# Create the contingency table for glucose level and diabetes, controlling for age group
contingency_table1 <- table(heartdata$glucose_level, heartdata$diabetes, heartdata$age_group)

# Display the contingency table
print(contingency_table1)
, ,  = middle-aged

             
                 0    1
  normal      3258   17
  prediabetes  204   11
  diabetes      16   44

, ,  = older

             
                 0    1
  normal       598    6
  prediabetes   49   11
  diabetes       6   20
# Conduct the Cochran-Mantel-Haenszel test
cmh_test_result <- mantelhaen.test(contingency_table1)

# Print the test results
cat("Cochran-Mantel-Haenszel Test Results:\n")
Cochran-Mantel-Haenszel Test Results:
print(cmh_test_result)

    Cochran-Mantel-Haenszel test

data:  contingency_table1
Cochran-Mantel-Haenszel M^2 = 1595.9, df = 2, p-value < 2.2e-16

Table 3. The correlation test between glucose level and diabetes, while controlling for age, was significant.

6.3 BMI Category vs Diabetes, Control for Glucose Level

Analysis of the correlation between BMI category and diabetes, while controlling for glucose level, was conducted (Table 4). The contingency tables are 4X2 ordered-nominal arrays. About 94% of cell counts are greater than or equal to 5 and all row totals are greater than or equal to 20 when combined over all tables. Therefore, the test is justified.

Ho: There is no association between BMI category and diabetes status when controlling for glucose level. Ha: There is a correlation between BMI category and diabetes status when controlling for glucose level.

Since the p-value is 0.01297, which is less than 0.05, we reject the null. There is evidence of a positive correlation between BMI category and diabetes when controlling for glucose level. As BMI category increases, the diabetes status increases.

# Convert BMI_category to an ordered factor
heartdata$BMI_category <- factor(heartdata$BMI_category, 
                                 levels = c("underweight", "healthy_weight", "overweight", "obese"),
                                 ordered = TRUE)

# Create the contingency table for BMI category and diabetes, controlling for glucose level
contingency_table <- table(heartdata$BMI_category, heartdata$diabetes, heartdata$glucose_level)

# Display the contingency table
print(contingency_table)
, ,  = normal

                
                    0    1
  underweight      51    2
  healthy_weight 1750    6
  overweight     1587   12
  obese           468    3

, ,  = prediabetes

                
                    0    1
  underweight       2    0
  healthy_weight   91    7
  overweight      120    7
  obese            40    8

, ,  = diabetes

                
                    0    1
  underweight       1    1
  healthy_weight   10   14
  overweight        9   30
  obese             2   19
# Conduct the Cochran-Mantel-Haenszel test
cmh_test_result <- mantelhaen.test(contingency_table)

# Print the test results
cat("Cochran-Mantel-Haenszel Test Results:\n")
Cochran-Mantel-Haenszel Test Results:
print(cmh_test_result)

    Cochran-Mantel-Haenszel test

data:  contingency_table
Cochran-Mantel-Haenszel M^2 = 11.096, df = 3, p-value = 0.01122

Table 4. The test of correlation between BMI category and diabetes, while controlling for glucose level, was significant.

6.4 BMI Category vs Diabetes, Control for Age Group

Analysis of the correlation between BMI category and diabetes, while controlling for age group, was also conducted. The contingency tables are 4X2 ordered-nominal arrays. All of the cell counts are greater than or equal to 5 when underweight and healthy_weight are collapsed and all row totals are greater than or equal to 20 when combined over all tables. Therefore, the test is justified.

Ho: There is no association between BMI category and diabetes status when controlling for age group. Ha: There is a correlation between BMI category and diabetes status when controlling for age group.

Since the p-value is approximately zero, which is less than 0.05, we reject the null. There is strong evidence of a positive correlation between BMI category and diabetes when controlling for age group. As BMI category increases, the diabetes status increases.

# Convert BMI_category to an ordered factor
heartdata$BMI_category <- factor(heartdata$BMI_category, 
                                 levels = c("underweight", "healthy_weight", "overweight", "obese"),
                                 ordered = TRUE)

# Create the contingency table for BMI category and diabetes, controlling for age group
contingency_table <- table(heartdata$BMI_category, heartdata$diabetes, heartdata$age_group)

# Display the contingency table
print(contingency_table)
, ,  = middle-aged

                
                    0    1
  underweight      48    2
  healthy_weight 1598   21
  overweight     1430   33
  obese           402   16

, ,  = older

                
                    0    1
  underweight       6    1
  healthy_weight  253    6
  overweight      286   16
  obese           108   14
# Conduct the Cochran-Mantel-Haenszel test
cmh_test_result <- mantelhaen.test(contingency_table)

# Print the test results
cat("Cochran-Mantel-Haenszel Test Results:\n")
Cochran-Mantel-Haenszel Test Results:
print(cmh_test_result)

    Cochran-Mantel-Haenszel test

data:  contingency_table
Cochran-Mantel-Haenszel M^2 = 26.396, df = 3, p-value = 7.878e-06

Table 5. A test of correlation between BMI category and diabetes, while controlling for age group was significant.

Based on the association analysis, glucose level is strongly correlated with diabetes, even when controlling for age, since age is associated with diabetes. Also, BMI Category is also strongly correlated with diabetes, even when controlling for age and glucose levels, since both factors are associated with diabetes.

7 LOGISTIC REGRESSION

7.1 Full model

The logistic regression analysis predicts Ten Year CHD, which is a binary response variable. The initial model contains all the variables in the original data subset.

# Convert TenYearCHD to a factor
heartdata$TenYearCHD <- factor(heartdata$TenYearCHD)

# Full logistic regression model
full.model <- glm(TenYearCHD ~ male + age + education + currentSmoker+ cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + totChol + BMI + sysBP + diaBP + heartRate+ glucose, 
             data = heartdata, 
             family = binomial)


# View the summary of the full model
summary(full.model)

Call:
glm(formula = TenYearCHD ~ male + age + education + currentSmoker + 
    cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + 
    totChol + BMI + sysBP + diaBP + heartRate + glucose, family = binomial, 
    data = heartdata)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9421  -0.5924  -0.4301  -0.2929   2.8407  

Coefficients:
                  Estimate Std. Error z value Pr(>|z|)    
(Intercept)      -7.967882   0.658427 -12.101  < 2e-16 ***
male1             0.477859   0.101314   4.717 2.40e-06 ***
age               0.060677   0.006283   9.658  < 2e-16 ***
education2       -0.205719   0.113679  -1.810 0.070351 .  
education3       -0.101183   0.139130  -0.727 0.467070    
education4        0.023775   0.152783   0.156 0.876340    
currentSmoker1    0.015494   0.144167   0.107 0.914414    
cigsPerDay        0.021244   0.005708   3.722 0.000198 ***
BPMeds1           0.262411   0.220981   1.187 0.235038    
prevalentStroke1  0.963913   0.444230   2.170 0.030018 *  
prevalentHyp1     0.227239   0.128746   1.765 0.077560 .  
diabetes1         0.204203   0.296206   0.689 0.490574    
totChol           0.001855   0.001028   1.805 0.071086 .  
BMI               0.002143   0.011830   0.181 0.856263    
sysBP             0.014126   0.003548   3.981 6.85e-05 ***
diaBP            -0.002814   0.005987  -0.470 0.638405    
heartRate        -0.001431   0.003889  -0.368 0.712949    
glucose           0.006627   0.002149   3.083 0.002050 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 3606.8  on 4233  degrees of freedom
Residual deviance: 3201.7  on 4216  degrees of freedom
  (6 observations deleted due to missingness)
AIC: 3237.7

Number of Fisher Scoring iterations: 5
coefficients <- summary(full.model)$coefficients
print_logit_equation <- function(full.model) {
    coefs <- summary(full.model)$coefficients
    intercept <- round(coefs[1, 1], 4)
    terms <- rownames(coefs)[-1]
    equations <- sapply(1:length(terms), function(i) {
        term_coef <- round(coefs[i + 1, 1], 4)
        paste0(term_coef, " * ", terms[i])
    })
    logit_equation <- paste("logit(p) =", intercept, "+", paste(equations, collapse = " + "))
    return(logit_equation)
}

logit_equation <- print_logit_equation(full.model)
cat(logit_equation, "\n")
logit(p) = -7.9679 + 0.4779 * male1 + 0.0607 * age + -0.2057 * education2 + -0.1012 * education3 + 0.0238 * education4 + 0.0155 * currentSmoker1 + 0.0212 * cigsPerDay + 0.2624 * BPMeds1 + 0.9639 * prevalentStroke1 + 0.2272 * prevalentHyp1 + 0.2042 * diabetes1 + 0.0019 * totChol + 0.0021 * BMI + 0.0141 * sysBP + -0.0028 * diaBP + -0.0014 * heartRate + 0.0066 * glucose 

The full model is below.

logit(p) = -7.9926 + 0.4841 * male1 + 0.0607 * age + -0.2277 * education2 + -0.0262 * education1 + -0.1244 * education3 + 0.0196 * currentSmoker1 + 0.0212 * cigsPerDay + 0.25 * BPMeds1 + 0.9678 * prevalentStroke1 + 0.2306 * prevalentHyp1 + 0.1793 * diabetes1 + 0.0018 * totChol + 0.0034 * BMI + 0.0142 * sysBP + -0.0031 * diaBP + -0.0012 * heartRate + 0.0067 * glucose

7.2 Trimmed Model

The full model had six significant predictor variables (male, age, cigsPerDay, prevalentStroke, sysBP, glucose). A trimmed model is created using these variables.

# Trimmed logistic regression model
trimmed.model <- glm(TenYearCHD ~ male + age + cigsPerDay + prevalentStroke + sysBP + glucose, 
             data = heartdata, 
             family = binomial)

# View the summary of the trimmed model
summary(trimmed.model)

Call:
glm(formula = TenYearCHD ~ male + age + cigsPerDay + prevalentStroke + 
    sysBP + glucose, family = binomial, data = heartdata)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.0459  -0.5915  -0.4358  -0.2997   2.7946  

Coefficients:
                  Estimate Std. Error z value Pr(>|z|)    
(Intercept)      -8.459095   0.389325 -21.728  < 2e-16 ***
male1             0.485088   0.097192   4.991 6.01e-07 ***
age               0.064763   0.005924  10.931  < 2e-16 ***
cigsPerDay        0.021445   0.003854   5.564 2.63e-08 ***
prevalentStroke1  1.046553   0.436186   2.399   0.0164 *  
sysBP             0.017055   0.002001   8.523  < 2e-16 ***
glucose           0.007537   0.001630   4.625 3.74e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 3612.2  on 4239  degrees of freedom
Residual deviance: 3219.1  on 4233  degrees of freedom
AIC: 3233.1

Number of Fisher Scoring iterations: 5

The trimmed model is below.

logit(p) = -8.4591 + 0.4851 * male1 + 0.0648 * age + 0.0214 * cigsPerDay + 1.047 * prevalentStroke1 + 0.0171 * sysBP + 0.0076 * glucose

The log odds of developing CHD in the next 10 years when all predictors are zero is -8.4591. This value is not meaningful in practical sense. It represents the logs odds value if all of the predictor variables can take on a value of zero.

Being male (compared to female) increases the log odds of developing CHD in the next 10 years by approximately 0.485088. The odds ratio is approximately 1.62. This means that males have about 62% higher odds of developing CHD in the next 10 years compared to females.

For each additional year in age, the log odds of developing CHD in the next 10 years increase by approximately 0.06477. The odds ratio is approximately 1.067. This indicates that with each additional year of age, the odds of developing CHD in the next 10 years increase by about 6.7%.

For each additional cigarette smoked per day, the log odds of developing CHD in the next 10 years increase by approximately 0.021428. The odds ratio is approximately 1.021. This suggests that each additional cigarette per day increases the odds of developing CHD in the next 10 years by about 2.2%.

Having a prevalent stroke increases the log odds of developing CHD in the next 10 years by approximately 1.047024. The odds ratio is approximately 2.85. This means that individuals who have had a stroke are about 2.85 times more likely to develop CHD in the next 10 years compared to those without a stroke.

For each one-unit (mmHg) increase in systolic blood pressure (sysBP), the log odds of developing CHD in the next 10 years increase by approximately 0.017064. The odds ratio is approximately 1.017. This indicates that each additional unit (mmHg) increase in sysBP increases the odds of developing CHD in the next 10 years by about 1.7%.

For each one-unit (mg/dL) increase in glucose level, the log odds of developing CHD in the next 10 years increase by approximately 0.007577. The odds ratio is approximately 1.008. This suggests that each additional unit increase (mg/dL) in glucose increases the odds of developing CHD in the next 10 years by about 0.8%.

Age and cigsPerDay were highly significant in the trimmed model. The feature engineer integrates both of these factors. In turn, a model is created using the feature engineer, smoking_risk_index in lieu of age and cigsPerDay.

fe.model <- glm(TenYearCHD ~ male + smoking_risk_index + prevalentStroke + sysBP + glucose, 
             data = heartdata, 
             family = binomial)

summary(fe.model)

Call:
glm(formula = TenYearCHD ~ male + smoking_risk_index + prevalentStroke + 
    sysBP + glucose, family = binomial, data = heartdata)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.8581  -0.5935  -0.4748  -0.3501   2.7487  

Coefficients:
                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)        -7.247697   0.384693 -18.840  < 2e-16 ***
male1               0.611129   0.090271   6.770 1.29e-11 ***
smoking_risk_index  0.015499   0.002798   5.539 3.04e-08 ***
prevalentStroke1    1.213351   0.427068   2.841   0.0045 ** 
sysBP               0.022027   0.001910  11.530  < 2e-16 ***
glucose             0.007982   0.001620   4.928 8.30e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 3612.2  on 4239  degrees of freedom
Residual deviance: 3324.3  on 4234  degrees of freedom
AIC: 3336.3

Number of Fisher Scoring iterations: 5

All variables are significant. This model has five variables, one less than the trimmed model.

logit(p) = -7.2477 + 0.6111 * male1 + 0.01550 * smoking_risk_index + 1.2134 * prevalentStroke1 + 0.0220 * sysBP + 0.0080 * glucose

The intercept of -7.2477 represents the log odds of developing CHD in the next 10 years when all predictor variables are equal to zero. However, the intercept value is not meaningful in this context.

The coefficient for male1 is 0.611129. This indicates that being male (compared to female) is associated with an increase in the log odds of developing CHD in the next 10 year. The odds ratio is approximately 1.84. This means that males have about 1.84 times the odds of developing CHD in the next 10 years compared to females, holding all other variables constant.

The coefficient for smoking_risk_index is 0.015499. This suggests that for each one-unit increase in the smoking risk index, the log odds of developing CHD in the next 10 year increase by 0.015499. The odds ratio is 1.015499. This means that for each additional point in the smoking risk index, the odds of developing CHD in the next 10 years increase by approximately 1.5%.

The coefficient for prevalentStroke1 is 1.213351. This indicates that having a prevalent stroke is associated with an increase in the log odds of developing CHD in the next 10 years by 1.213351. The odds ratio is approximately 3.36. This means that individuals with a prevalent stroke have about 3.36 times the odds of developing CHD in the next 10 years compared to those without.

The coefficient for sysBP is 0.022035. This means that for each one-unit (mmHg) increase in systolic blood pressure, the log odds of developing CHD in the next 10 years increase by 0.022035. The odds ratio is approximately 1.0223. This suggests that for each unit (mmHg) increase in systolic blood pressure, the odds of developing CHD in the next 10 years increase by approximately 2.2%.

The coefficient for glucose is 0.007982. This indicates that for each one-unit (mg/dL) increase in glucose level, the log odds of developing CHD in the next 10 years increase by 0.007982. The odds ratio is approximately 1.008. This means that for each unit (mg/dL) increase in glucose, the odds of developing CHD in the next 10 years increase by about 0.8%.

8 LOGISTIC MODEL DIAGNOSTICS

The trimmed model and the feature engineer model were compared using ROC curve. The area under the curve for the trimmed model, 0.729, is greater than the area under the curve for the feature engineer model, 0.629.

# Get predicted probabilities
trimmed_prob <- predict(trimmed.model, type = "response")
fe_prob <- predict(fe.model, type = "response")

# Calculate residuals
trimmed_residuals <- residuals(trimmed.model, type = "deviance")
fe_residuals <- residuals(fe.model, type = "deviance")


# ROC Curve for trimmed.model
roc_trimmed <- roc(heartdata$TenYearCHD, trimmed_prob)
plot(roc_trimmed, main = "ROC Curve for Trimmed Model")

auc(roc_trimmed)
Area under the curve: 0.7289
# ROC Curve for fe.model
roc_fe <- roc(heartdata$TenYearCHD, fe_prob)
plot(roc_fe, main = "ROC Curve for Feature Engineered Model")

auc(roc_fe)
Area under the curve: 0.692

Figure HH. The trimmed model performed better than the feature engineer model.

Accuracy, sensitivity, and specificity were compared for both models as part of the diagnostics. Table 6 summarizes the findings.

  1. Accuracy

    • Trimmed Model: 85.47% • Feature Engineered Model: 85.05%

Accuracy represents the overall proportion of correct predictions (both true positives and true negatives) out of all predictions made. In both models, accuracy is high (around 85%), meaning they correctly classify about 85% of all cases.

  1. Sensitivity (Recall)

    • Trimmed Model: 8.23% • Feature Engineered Model: 4.81%

Sensitivity (or recall) measures how well the model identifies actual positives (TenYearCHD = 1) which is the proportion of true positives out of all actual positives (TP / [TP + FN]).

•   Trimmed Model: This model only identifies about 8.23% of actual positive cases. The model struggles to correctly identify most cases where TenYearCHD = 1.
•   Feature Engineered Model: Sensitivity is even lower at 4.81%, indicating an even greater difficulty in identifying true positives (when TenYearCHD = 1).
  1. Specificity

    • Trimmed Model: 99.3% • Feature Engineered Model: 99.42%

Specificity measures the model’s ability to correctly identify actual negatives (correctly predicting cases where TenYearCHD = 0). It’s the proportion of true negatives out of all actual negatives (TN / [TN + FP]).

•   Trimmed Model: 99.28% of actual negative cases are correctly classified, meaning it’s highly effective at identifying cases where TenYearCHD = 0.
•   Feature Engineered Model: 99.42%, even better at identifying negatives, but only slightly higher.

Both models have high specificity and are very good at identifying cases where TenYearCHD = 0. However, both models have low sensitivity, meaning they are poor at identifying cases where TenYearCHD = 1. This may have occurred because less than 20% of observations had TenYearCHD = 1 (positive case).

# Assuming you have your models trimmed.model and fe.model already defined

# Step 1: Predict probabilities for both models
heartdata$predicted_trimmed <- predict(trimmed.model, newdata = heartdata, type = "response")
heartdata$predicted_fe <- predict(fe.model, newdata = heartdata, type = "response")

# Step 2: Convert predicted probabilities to binary outcomes (0 or 1)
heartdata$predicted_trimmed_outcome <- ifelse(heartdata$predicted_trimmed > 0.5, 1, 0)
heartdata$predicted_fe_outcome <- ifelse(heartdata$predicted_fe > 0.5, 1, 0)

# Step 3: Compare predicted outcomes to actual TenYearCHD values
# Calculate accuracy, sensitivity, specificity, etc., for both models

# Function to calculate metrics
calculate_metrics <- function(actual, predicted) {
  confusion_matrix <- table(actual, predicted)
  accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
  sensitivity <- confusion_matrix[2, 2] / sum(confusion_matrix[2, ])
  specificity <- confusion_matrix[1, 1] / sum(confusion_matrix[1, ])
  
  list(accuracy = accuracy, sensitivity = sensitivity, specificity = specificity)
}

# Metrics for trimmed.model
metrics_trimmed <- calculate_metrics(heartdata$TenYearCHD, heartdata$predicted_trimmed_outcome)
cat("Trimmed Model Metrics:\n")
Trimmed Model Metrics:
print(metrics_trimmed)
$accuracy
[1] 0.854717

$sensitivity
[1] 0.08229814

$specificity
[1] 0.9930478
# Metrics for fe.model
metrics_fe <- calculate_metrics(heartdata$TenYearCHD, heartdata$predicted_fe_outcome)
cat("Feature Engineered Model Metrics:\n")
Feature Engineered Model Metrics:
print(metrics_fe)
$accuracy
[1] 0.8504717

$sensitivity
[1] 0.04813665

$specificity
[1] 0.9941602

Table 6. Both models have similar accuracy at about 85%. However, the trimmed model had higher sensitivity than the feature engineer model. The feature engineer model had slightly higher specificity than the trimmed model.

9 CONCLUSION

Most of the variables in this Framingham Heart Study data subset are known to be associated with heart disease, specifically CHD. As people age, the risk of developing heart disease increases due to various biological and behavioral factors. High blood pressure damages the heart and blood vessels, increasing the risk of heart disease. Individuals that are on blood pressure medication, a variable in this study, is an indication of high blood pressure. A higher resting heart rate is associated with heart disease because the heart is enduring a higher workload. Cholesterol levels are associated with developing CHD. Specifically, high levels of LDL cholesterol is strongly associated with heart disease. HDL cholesterol are protective against heart disease. However, the HDL variable was not included in this data subset. Smoking damages bloods vessels which increases the risk of heart disease. High blood glucose levels (indication of possible diabetes), can damage blood vessels and nerves controlling the heart. High BMI indicates excess weight which is associated with increased blood pressure, cholesterol levels, and risk of diabetes which are all factors of CHD. Males have a higher overall risk of developing heart disease earlier than females due to various reasons including hormones and genetics. (https://www.cdc.gov/heart-disease/risk-factors/index.html). Many of these findings are validated in this analysis.

The goal of this analysis was to find significant associations and predictors for CHD. Significant evidence of association between sex and TenYearCHD was established through Analysis of Variance. In fact, males had higher outcomes of TenYearCHD than females. As well, significant evidence of association was found for blood pressure, both systolic and diastolic, for smokers and non-smokers. Smokers have lower blood pressure likely due to the vasodilatory effects of nicotine.

A Chi Square general test of association between sex and TenYearCHD indicated a significant association. In fact, men tend to have positive TenYearCHD outcomes more than women. A general tests of association resulted in significant correlations between glucose level and diabetes while controlling for age. As well, BMI and diabetes had significant correlation while controlling for glucose and age.

Logistic regression models were created to predict TenYearCHD. The trimmed model had the best performance using sex, age, cigarettes per day, prevalent stroke, systolic blood pressure, and glucose as factors. The models did not predict the positive outcomes very well. According to the Framingham Heart Study, the predictors used in their model includes age, sex, race, total cholesterol, HDL cholesterol, systolic blood pressure, blood pressure lowering medication use, diabetes, and smoking. (https://www.framinghamheartstudy.org/fhs-risk-functions/cardiovascular-disease-10-year-risk/). Two of the factors, race and HDL cholesterol, were not available in the data subset used for this analysis. These missing factors may contribute to low sensitivity. As well, the accuracy is extremely high possibly because the model is predominantly predicting the majority group of TenYearCHD, which is the negative cases.

Overall, key relationships between factors were confirmed in this analysis. Benchmarking against the Framingham 10-year risk calculator (https://www.framinghamheartstudy.org/fhs-risk-functions/cardiovascular-disease-10-year-risk/), factors were successfully identified in this analysis as being significant predictors of TenYearCHD.

10 WORKS CITED

Centers for Disease Control and Prevention. (n.d.). Defining adult overweight & obesity. U.S. Department of Health and Human Services. https://www.cdc.gov/bmi/adult-calculator/bmi-categories.html

Centers for Disease Control and Prevention. (n.d.). Diabetes tests. U.S. Department of Health and Human Services. https://www.cdc.gov/diabetes/diabetes-testing/index.htm

Centers for Disease Control and Prevention. (2023, April 13). Risk factors for heart disease. https://www.cdc.gov/heart-disease/risk-factors/index.html

Davies B. Healthcare Priorities: The “Young” and the “Old”. Camb Q Healthc Ethics. 2022 Nov 10;32(2):1-12. doi: 10.1017/S0963180122000299. Epub ahead of print. PMID: 36352770; PMCID: PMC10425921.

Framingham Heart Study. (n.d.). Cardiovascular disease 10-year risk. Retrieved October 22, 2024, from https://www.framinghamheartstudy.org/fhs-risk-functions/cardiovascular-disease-10-year-risk/

Sharma, R., Kaur, R., Kaur, G., Singh, K., Prabhakar, A., Sharma, A., & Kaur, A. (2022). Emerging roles of gut microbiota in health and disease: A review. Frontiers in Microbiology, 13, Article 799848. https://doi.org/10.3389/fmicb.2022.799848

LS0tCnRpdGxlOiAnRnJhbWluZ2hhbSBIZWFydCBTdHVkeTogUmlzayBvZiBDb3JvbmFyeSBIZWFydCBEaXNlYXNlICYgRmFjdG9yIEFzc29jaWF0aW9uIEFuYWx5c2lzIHVzaW5nIGEgRGF0YSBTdWJzZXQnCmF1dGhvcjogIkpvYW5uZSBNdXNhIgpkYXRlOiAiT2N0b2JlciAyMywgMjAyNCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICBmaWdfd2lkdGg6IDMKICAgIGZpZ19oZWlnaHQ6IDMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7PWh0bWx9Cgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovCgpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLwogIGZvbnQtc2l6ZTogMjRweDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9Cmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8KICBmb250LXNpemU6IDIwcHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IERhcmtCbHVlOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAyMnB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CgppZiAoIXJlcXVpcmUoIk1BU1MiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikKICAgbGlicmFyeShNQVNTKQp9CmlmICghcmVxdWlyZSgibGVhZmxldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKQogICBsaWJyYXJ5KGxlYWZsZXQpCn0KCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpCmxpYnJhcnkoZ2dyaWRnZXMpCn0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKfQoKaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiAgIGxpYnJhcnkoZ2dwbG90MikKfQppZiAoIXJlcXVpcmUoIm5uZXQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJubmV0IikKICAgbGlicmFyeShubmV0KQp9CiAKaWYgKCFyZXF1aXJlKCJub3J0ZXN0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygibm9ydGVzdCIpCiAgIGxpYnJhcnkobm9ydGVzdCkKfSAKCmlmICghcmVxdWlyZSgicFJPQyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQogICBsaWJyYXJ5KHBST0MpCn0gCgppZiAoIXJlcXVpcmUoImNvcnJwbG90IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQogICBsaWJyYXJ5KGNvcnJwbG90KQp9IAoKaWYgKCFyZXF1aXJlKCJwYXRjaHdvcmsiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJwYXRjaHdvcmsiKQogICBsaWJyYXJ5KHBhdGNod29yaykKfSAKCmlmICghcmVxdWlyZSgiY2FyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyIikKICAgbGlicmFyeShjYXIpCn0gCgppZiAoIXJlcXVpcmUoImdyaWRFeHRyYSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpCiAgIGxpYnJhcnkoZ3JpZEV4dHJhKQp9IAoKaWYgKCFyZXF1aXJlKCJnZ21vc2FpYyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImdnbW9zYWljIikKICAgbGlicmFyeShnZ21vc2FpYykKfSAKCmlmICghcmVxdWlyZSgiUmVzb3VyY2VTZWxlY3Rpb24iKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJSZXNvdXJjZVNlbGVjdGlvbiIpCiAgIGxpYnJhcnkoUmVzb3VyY2VTZWxlY3Rpb24pCn0KCmlmICghcmVxdWlyZSgicmVzaGFwZTIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJyZXNoYXBlMiIpCiAgIGxpYnJhcnkocmVzaGFwZTIpCn0gCgppZiAoIXJlcXVpcmUoImxhdHRpY2UiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikKICAgbGlicmFyeShsYXR0aWNlKQp9IAoKaWYgKCFyZXF1aXJlKCJ0aW55dGV4IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlueXRleCIpCiAgIGxpYnJhcnkodGlueXRleCkKfSAKCmlmICghcmVxdWlyZSgidmNkIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidmNkIikKICAgbGlicmFyeSh2Y2QpCn0gCgppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQogICBsaWJyYXJ5KGNhcmV0KQp9CgoKCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgICAgICAgICAgICAgICAgICAgIGVjaG8gPSBUUlVFLCAgIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgICAKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQogICAgICAgICAgICAgICAgICAgICAgKSAgCmBgYAoKIyBJTlRST0RVQ1RJT04KClRoZSBGcmFtaW5naGFtIEhlYXJ0IHN0dWR5IHN0YXJ0ZWQgaW4gMTk0OCB0byBpZGVudGlmeSBmYWN0b3JzIG9yIGNoYXJhY3RlcmlzdGljcyB0aGF0IGNvbnRyaWJ1dGUgdG8gY2FyZGlvdmFzY3VsYXIgZGlzZWFzZS4gVGhlIHN0dWR5IGlzIGJhc2VkIGluIE1hc3NhY2h1c2V0dHMgYW5kIGhhcyBmb2xsb3dlZCBtdWx0aXBsZSBnZW5lcmF0aW9ucyBvZiBwYXJ0aWNpcGFudHMuIFRoZSBzdHVkeSBpcyBhIGxvbmdpdHVkaW5hbCBkZXNpZ24gdGhhdCBjb2xsZWN0cyBkYXRhIHZpYSBleGFtaW5hdGlvbnMgYW5kIGhlYWx0aCBhc3Nlc3NtZW50cy4gQXMgd2VsbCwgcGFydGljaXBhbnRzIHVuZGVyZ28gbWVkaWNhbCBldmFsdWF0aW9ucywgbGFib3JhdG9yeSB0ZXN0cywgYW5kIGxpZmVzdHlsZSBhc3Nlc3NtZW50cy4gU29tZSBvZiB0aGVzZSByaXNrIGZhY3RvcnMgYXJlIGJlaGF2aW9yYWwgYW5kIHNvbWUgYXJlIGJpb2xvZ2ljYWwuIFRoaXMgYW5hbHlzaXMgY29uc2lkZXJzIGEgZGF0YSBzdWJzZXQgb2YgdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuIAoKClRoaXMgYW5hbHlzaXMgd2lsbCBleGFtaW5lIHRoZSBhc3NvY2lhdGlvbiBvZiBzb21lIG9mIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgYXNzb2NpYXRlZCB3aXRoIGRpYWJldGVzIGFuZCBzbW9raW5nLiBBcyB3ZWxsLCBpbnZlc3RpZ2F0aW9uIGludG8gaG93IHNvbWUgb2YgdGhlc2UgZmFjdG9ycyBwcmVkaWN0IHRoZSBsaWtlbGlob29kIG9mIGRldmVsb3BpbmcgY29yb25hcnkgaGVhcnQgZGlzZWFzZSB3aXRoaW4gdGVuIHllYXJzIGFtb25nIHBhcnRpY2lwYW50cyBpbiB0aGUgRnJhbWluZ2hhbSBIZWFydCBTdHVkeSB3aWxsIGJlIGNvbmR1Y3RlZC4gCgoKIyBGUkFNSU5HSEFNIERBVEEgU1VCU0VUCgojIyBEZXNjcmlwdGlvbiBvZiBEYXRhCgoKVGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgaW5jbHVkZXMgZGF0YSBmcm9tIGFib3V0IDE1LDAwMCBwYXJ0aWNpcGFudHMgYW5kIG92ZXIgMSwwMDAgdmFyaWFibGVzLiBUaGUgZGF0YSBzZXQgdXNlZCBmb3IgdGhpcyBhbmFseXNpcyBpcyBhIHN1YnNldCBjb250YWluaW5nIDQyNDAgb2JzZXJ2YXRpb25zIGFuZCAxNiB2YXJpYWJsZXMuIE9mIHRoZSAxNiB2YXJpYWJsZXMsIDggdmFyaWFibGVzIGFyZSBmYWN0b3IgdmFyaWFibGVzIGFuZCA3IGFyZSBudW1lcmljIHZhcmlhYmxlcy4gCgpUaGUgbGlzdCBvZiB2YXJpYWJsZSBuYW1lcyBhbmQgdGhlaXIgZGVzY3JpcHRpb24vZGVmaW5pdGlvbnMgaW4gdGhpcyBkYXRhIHN1YnNldCBpcyBsaXN0ZWQgYmVsb3cuCgoxLiBzZXggKGZhY3Rvcik6IHRoZSBzZXggb2YgdGhlIG9ic2VydmF0aW9ucy4gVGhlIHZhcmlhYmxlIGlzIGEgYmluYXJ5IG5hbWVkIOKAnG1hbGXigJ0gaW4gdGhlIGRhdGFzZXQuIAoyLiBhZ2UgKG51bWVyaWNhbCk6IEFnZSBhdCB0aGUgdGltZSBvZiBtZWRpY2FsIGV4YW1pbmF0aW9uIGluIHllYXJzLgozLiBlZHVjYXRpb24gKGZhY3Rvcik6IEEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb2YgdGhlIHBhcnRpY2lwYW50cyBlZHVjYXRpb24sIHdpdGggdGhlIGxldmVsczogU29tZSBoaWdoIHNjaG9vbCAoMSksIGhpZ2ggICAgICBzY2hvb2wvR0VEICgyKSwgc29tZSBjb2xsZWdlL3ZvY2F0aW9uYWwgc2Nob29sICgzKSwgY29sbGVnZSAoNCkgCjQuIGN1cnJlbnRTbW9rZXIgKGZhY3Rvcik6IEN1cnJlbnQgY2lnYXJldHRlIHNtb2tpbmcgYXQgdGhlIHRpbWUgb2YgZXhhbWluYXRpb25zIAo1LiBjaWdzUGVyRGF5IChudW1lcmljYWwpOiBOdW1iZXIgb2YgY2lnYXJldHRlcyBzbW9rZWQgZWFjaCBkYXkgCjYuIEJQbWVkcyAoZmFjdG9yKTogVXNlIG9mIEFudGktaHlwZXJ0ZW5zaXZlIG1lZGljYXRpb24gYXQgZXhhbSAKNy4gcHJldmFsZW50U3Ryb2tlIChmYWN0b3IpOiBQcmV2YWxlbnQgU3Ryb2tlICgwID0gZnJlZSBvZiBkaXNlYXNlKSAKOC4gcHJldmFsZW50SHlwIChmYWN0b3IpOiBQcmV2YWxlbnQgSHlwZXJ0ZW5zaXZlLiBTdWJqZWN0IHdhcyBkZWZpbmVkIGFzIGh5cGVydGVuc2l2ZSBpZiB0cmVhdGVkIAo5LiBkaWFiZXRlcyAoZmFjdG9yKTogRGlhYmV0aWMgYWNjb3JkaW5nIHRvIGNyaXRlcmlhIG9mIGZpcnN0IGV4YW0gdHJlYXRlZCAKMTAuIHRvdENob2wgKG51bWVyaWNhbCk6IFRvdGFsIGNob2xlc3Rlcm9sIChtZy9kTCkgCjExLiBzeXNCUCAobnVtZXJpY2FsKTogU3lzdG9saWMgQmxvb2QgUHJlc3N1cmUgKG1tSGcpIAoxMi4gZGlhQlAgKG51bWVyaWNhbCk6IERpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSAobW1IZykgCjEzLiBCTUkgKG51bWVyaWNhbCk6IEJvZHkgTWFzcyBJbmRleCwgd2VpZ2h0IChrZykvaGVpZ2h0IChtKV4yIAoxNC4gaGVhcnRSYXRlIChudW1lcmljYWwpOiBIZWFydCByYXRlIChiZWF0cy9taW51dGUpIAoxNS4gZ2x1Y29zZSAobnVtZXJpY2FsKTogQmxvb2QgZ2x1Y29zZSBsZXZlbCAobWcvZEwpIAoxNi4gVGVuWWVhckNIRCAoZmFjdG9yLCByZXNwb25zZSB2YXJpYWJsZSk6IFRoZSAxMCB5ZWFyIHJpc2sgb2YgY29yb25hcnkgaGVhcnQgZGlzZWFzZShDSEQpLgoKYGBge3J9CgpoZWFydGRhdGEgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qbXVzYTMvam11c2EvcmVmcy9oZWFkcy9tYWluL0ZyYW1pbmdoYW1IZWFydFN0dWR5LmNzdiIpCgoKIyBDb252ZXJ0IHNvbWUgdmFyaWFibGVzIHRvIGZhY3RvcnMKaGVhcnRkYXRhIDwtIGhlYXJ0ZGF0YSAlPiUKICBtdXRhdGUoCiAgICBtYWxlID0gYXMuZmFjdG9yKG1hbGUpLAogICAgZWR1Y2F0aW9uID0gYXMuZmFjdG9yKGVkdWNhdGlvbiksCiAgICBjdXJyZW50U21va2VyID0gYXMuZmFjdG9yKGN1cnJlbnRTbW9rZXIpLAogICAgQlBNZWRzID0gYXMuZmFjdG9yKEJQTWVkcyksCiAgICBwcmV2YWxlbnRTdHJva2UgPSBhcy5mYWN0b3IocHJldmFsZW50U3Ryb2tlKSwKICAgIHByZXZhbGVudEh5cCA9IGFzLmZhY3RvcihwcmV2YWxlbnRIeXApLAogICAgZGlhYmV0ZXMgPSBhcy5mYWN0b3IoZGlhYmV0ZXMpLAogICAgVGVuWWVhckNIRCA9IGFzLmZhY3RvcihUZW5ZZWFyQ0hEKQogICkKc3VtbWFyeShoZWFydGRhdGEpCgojIEZpbmQgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgKHJvd3MpIGluIGhlYXJ0ZGF0YQojdG90YWxfb2JzZXJ2YXRpb25zIDwtIG5yb3coaGVhcnRkYXRhKQojdG90YWxfb2JzZXJ2YXRpb25zCgojIEZpbmQgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyB0aGF0IGhhdmUgYXQgbGVhc3Qgb25lIG1pc3NpbmcgdmFsdWUKI21pc3Npbmdfcm93cyA8LSBhcHBseShoZWFydGRhdGEsIDEsIGZ1bmN0aW9uKHgpIGFueShpcy5uYSh4KSkpCgojc3VtKG1pc3Npbmdfcm93cykKYGBgCgpUYWJsZSAxLiBTdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSAxNiB2YXJpYWJsZXMgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgc3Vic2V0LiBTZXZlbiBvZiB0aGUgdmFyaWFibGVzIGhhdmUgbWlzc2luZyB2YWx1ZXMuIE1hbGUsIGVkdWNhdGlvbiwgY3VycmVudFNtb2tlciwgQlBNZWRzLCBwcmV2YWxlbnRTbW9rZXIsIHByZXZhbGVudEh5cCwgZGlhYmV0ZXMsIGFuZCBUZW5ZZWFyQ0hEIHdlcmUgY29udmVydGVkIHRvIGZhY3RvciB2YXJpYWJsZXMuIAoKCiMjIE1pc3NpbmcgVmFsdWVzCgpUaGUgZGF0YSBzdWJzZXQgY29udGFpbmVkIG1pc3NpbmcgdmFsdWVzLiBUYWJsZSAyIGRpc3BsYXlzIHRoZSBjb3VudHMgZm9yIG1pc3NpbmcgdmFsdWVzLiBUaGVyZSB3ZXJlIDEwNSBtaXNzaW5nIHZhbHVlcyBmb3IgZWR1Y2F0aW9uLCAyOSBtaXNzaW5nIHZhbHVlcyBmb3IgY2lnc1BlckRheSwgNTMgbWlzc2luZyB2YWx1ZXMgZm9yIEJQTWVkcywgNTAgbWlzc2luZyB2YWx1ZXMgZm9yIHRvdENob2wsIDE5IG1pc3NpbmcgdmFsdWVzIGZvciBCTUksIDEgbWlzc2luZyB2YWx1ZSBmb3IgaGVhcnRSYXRlLCBhbmQgMzg4IG1pc3NpbmcgdmFsdWVzIGZvciBnbHVjb3NlLiBPZiB0aGUgNCwyNDAgb2JzZXJ2YXRpb25zIGluIHRoaXMgZGF0YSBzdWJzZXQsIDU4MiBvYnNlcnZhdGlvbnMgaGFkIGF0IGxlYXN0IG9uZSBtaXNzaW5nIHZhbHVlLiAKClNldmVyYWwgc3RlcHMgd2VyZSB0YWtlbiB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMuCgpgYGB7cn0KCiMgQ291bnQgbWlzc2luZyB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUgCm1pc3NpbmdfdmFsdWVzIDwtIGNvbFN1bXMoaXMubmEoaGVhcnRkYXRhKSkKCiMgRGlzcGxheSB0aGUgcmVzdWx0CnByaW50KG1pc3NpbmdfdmFsdWVzKQoKI2VkdWNhdGlvbiBoYXMgMTA1LCBjaWdzUGVyRGF5IGhhcyAyOSwgdG90Q2hvbCBoYXMgNTAsIEJQTWVkcyBoYXMgNTMsIEJNSSBoYXMgMTksIGhlYXJ0UmF0ZSBoYXMgMSwgZ2x1Y29zZSBoYXMgMzg4IG1pc3NpbmcgdmFsdWVzCgojbnVtYmVyIG9mIHJvd3MgYmVmb3JlIHN1YnNldHRpbmcKI251bV9vYnNlcnZhdGlvbnMgPC0gbnJvdyhoZWFydGRhdGEpCiNwcmludChudW1fb2JzZXJ2YXRpb25zKQojZHMgaGFzIDQyNDAgcm93cwoKCmBgYApUYWJsZSAyLiBBIGNvdW50IG9mIG1pc3NpbmcgdmFsdWVzIGJ5IHZhcmlhYmxlcy4gCgoKRmlyc3QsIHRoZSBkYXRhIHdhcyBzdWJzZXR0ZWQgdG8gaW5jbHVkZSB0aGUgMyw2NTggY29tcGxldGUgb2JzZXJ2YXRpb25zLiBGcm9tIHRoZSBjb21wbGV0ZSBkYXRhIHNldCwgYSBtdWx0aW5vbWlhbCByZWdyZXNzaW9uIG1vZGVsIHdhcyBjcmVhdGVkLiBUaGUgbW9kZWwgdGhlbiBwcmVkaWN0ZWQgdmFsdWVzIGFuZCBpbXB1dGVkIHRoZXNlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIG1pc3NpbmcgZWR1Y2F0aW9uIGRhdGEgaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0LiBBZnRlciB0aGlzIHByb2Nlc3Mgd2FzIGNvbXBsZXRlZCwgdGhlcmUgd2VyZSAzLDc0NCBvYnNlcnZhdGlvbnMgd2l0aCBubyBtaXNzaW5nIHZhbHVlcy4gCgoKYGBge3J9CgojIFN1YnNldCB0aGUgZGF0YXNldCB0byByZW1vdmUgcm93cyB3aXRoIGFueSBtaXNzaW5nIHZhbHVlcyB0byBjcmVhdGUgcmVncmVzc2lvbiBtb2RlbHMgZm9yIG1pc3NpbmcgdmFsdWVzCgpoZWFydF9jbGVhbiA8LSBuYS5vbWl0KGhlYXJ0ZGF0YSkKCiMgRGlzcGxheSB0aGUgY2xlYW5lZCBkYXRhc2V0CiNwcmludChoZWFydF9jbGVhbikKCiNudW1iZXIgb2Ygcm93cyBhZnRlciBzdWJzZXR0aW5nCiNudW1fb2JzZXJ2YXRpb25zIDwtIG5yb3coaGVhcnRfY2xlYW4pCiNwcmludChudW1fb2JzZXJ2YXRpb25zKQoKCiNNVUxUSU5PTUlBTCBSRUdSRVNTSU9OIEZPUiBFRFVDQVRJT04gTUlTU0lORyBWQUxVRVMgCgojIENvbnZlcnQgZWR1Y2F0aW9uIHRvIGEgZmFjdG9yCmhlYXJ0X2NsZWFuJGVkdWNhdGlvbiA8LSBhcy5mYWN0b3IoaGVhcnRfY2xlYW4kZWR1Y2F0aW9uKQoKIyBDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMgaW4gZWR1Y2F0aW9uCiN0YWJsZShpcy5uYShoZWFydF9jbGVhbiRlZHVjYXRpb24pKQoKIyBGaXQgdGhlIG11bHRpbm9taWFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwKbW9kZWxfbXVsdGlub21pYWwgPC0gbXVsdGlub20oZWR1Y2F0aW9uIH4gYWdlICsgbWFsZSArIGdsdWNvc2UgKyBjaWdzUGVyRGF5LCBkYXRhID0gaGVhcnRfY2xlYW4pCgojIFN1bW1hcnkgb2YgdGhlIG1vZGVsCnN1bW1hcnkobW9kZWxfbXVsdGlub21pYWwpCgojIEdldCB0aGUgY29lZmZpY2llbnRzCmNvZWYobW9kZWxfbXVsdGlub21pYWwpCgojIENhbGN1bGF0ZSBvZGRzIHJhdGlvcwpvZGRzX3JhdGlvcyA8LSBleHAoY29lZihtb2RlbF9tdWx0aW5vbWlhbCkpCm9kZHNfcmF0aW9zCgojIENyZWF0ZSBhIGxvZ2ljYWwgdmVjdG9yIHRvIGlkZW50aWZ5IG1pc3NpbmcgZWR1Y2F0aW9uIHZhbHVlcwptaXNzaW5nX3Jvd3MgPC0gaXMubmEoaGVhcnRkYXRhJGVkdWNhdGlvbikKCiMgQ3JlYXRlIG5ld2RhdGEgZm9yIHByZWRpY3Rpb24gKG9ubHkgaW5jbHVkZSByb3dzIHdpdGhvdXQgbWlzc2luZyBlZHVjYXRpb24gdmFsdWVzKQpuZXdkYXRhIDwtIGhlYXJ0ZGF0YVshbWlzc2luZ19yb3dzLF0KCiMgUHJlZGljdCBlZHVjYXRpb24gbGV2ZWxzCnByZWRpY3RlZF9jbGFzc2VzIDwtIHByZWRpY3QobW9kZWxfbXVsdGlub21pYWwsIG5ld2RhdGEgPSBuZXdkYXRhKQoKIyBGaWxsIGluIHRoZSBtaXNzaW5nIGVkdWNhdGlvbiB2YWx1ZXMgaW4gaGVhcnRkYXRhCmhlYXJ0ZGF0YSRlZHVjYXRpb25bbWlzc2luZ19yb3dzXSA8LSBwcmVkaWN0ZWRfY2xhc3NlcwoKIyBWaWV3IHRoZSB1cGRhdGVkIGhlYXJ0ZGF0YQojaGVhZChoZWFydGRhdGEpCiNzdW1tYXJ5KGhlYXJ0ZGF0YSkKCiNubyBtaXNzaW5nIHZhbHVlcyBmb3IgZWR1Y2F0aW9uCmBgYAoKU2Vjb25kLCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2FzIGNyZWF0ZWQgdXNlZCB0aGUgY29tcGxldGUgZGF0YSBzZXQgdG8gcHJlZGljdCBCUE1lZHMuIFRoZSBtb2RlbCB3YXMgdXNlZCB0byBpbXB1dGUgdGhlIG1pc3NpbmcgQlBNZWRzIGRhdGEgaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0LiAKCgpgYGB7cn0KCiNMT0dJU1RJQ0FMIFJFR1JFU1NJT04gVE8gSU1QVVRFIE1JU1NJTkcgQlBNRURTCgojIFN0ZXAgMTogSWRlbnRpZnkgbWlzc2luZyByb3dzIGluIEJQTWVkcwptaXNzaW5nX3Jvd3MgPC0gaXMubmEoaGVhcnRkYXRhJEJQTWVkcykKCiMgU3RlcCAyOiBDcmVhdGUgbmV3ZGF0YSBmb3IgcHJlZGljdGlvbiAob25seSBpbmNsdWRlIHJvd3Mgd2l0aG91dCBtaXNzaW5nIEJQTWVkcykKIyBVc2UgY29tcGxldGUgY2FzZXMgZm9yIGZpdHRpbmcgdGhlIG1vZGVsIGZyb20gaGVhcnRfY2xlYW4KY29tcGxldGVfY2FzZXMgPC0gaGVhcnRfY2xlYW5bIWlzLm5hKGhlYXJ0X2NsZWFuJEJQTWVkcyksIF0KCiMgU3RlcCAzOiBGaXQgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgKGFzc3VtaW5nIHlvdSBoYXZlIHJlbGV2YW50IHByZWRpY3RvcnMpCm1vZGVsX2xvZ2lzdGljIDwtIGdsbShCUE1lZHMgfiBhZ2UgKyBtYWxlICsgcHJldmFsZW50SHlwICsgc3lzQlAgKyBkaWFCUCwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY29tcGxldGVfY2FzZXMsIAogICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoKSkKCiMgU3RlcCA0OiBQcmVkaWN0IGZvciBhbGwgY2FzZXMgaW4gaGVhcnRkYXRhIGluY2x1ZGluZyB0aGUgbWlzc2luZyBvbmVzCiMgVXNlIGhlYXJ0ZGF0YSBmb3IgcHJlZGljdGlvbiwgZW5zdXJpbmcgaXQgaW5jbHVkZXMgYWxsIHJlbGV2YW50IHByZWRpY3RvcnMKcHJlZGljdGVkX3Byb2JzIDwtIHByZWRpY3QobW9kZWxfbG9naXN0aWMsIG5ld2RhdGEgPSBoZWFydGRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBTdGVwIDU6IENsYXNzaWZ5IHByb2JhYmlsaXRpZXMgaW50byBjbGFzc2VzICgwIG9yIDEpCnByZWRpY3RlZF9jbGFzc2VzIDwtIGlmZWxzZShwcmVkaWN0ZWRfcHJvYnMgPiAwLjUsIDEsIDApCgojIFN0ZXAgNjogRmlsbCBpbiB0aGUgbWlzc2luZyBCUE1lZHMgdmFsdWVzIHVzaW5nIHRoZSBwcmVkaWN0aW9ucwojIE1ha2Ugc3VyZSB5b3Ugb25seSBmaWxsIGluIHRoZSBtaXNzaW5nIHZhbHVlcwpoZWFydGRhdGEkQlBNZWRzW21pc3Npbmdfcm93c10gPC0gcHJlZGljdGVkX2NsYXNzZXNbbWlzc2luZ19yb3dzXQoKIyBTdGVwIDc6IENoZWNrIHRoZSByZXN1bHRzCiNoZWFkKGhlYXJ0ZGF0YSkKI2NhdCgiUmVtYWluaW5nIG1pc3NpbmcgdmFsdWVzIGluIEJQTWVkczoiLCBzdW0oaXMubmEoaGVhcnRkYXRhJEJQTWVkcykpLCAiXG4iKQoKI3N1bW1hcnkoaGVhcnRkYXRhKQoKI0VORCBMT0dJU1RJQyBSRUdSRVNTSU9OCgpgYGAKCgpUaGlyZCwgcmVncmVzc2lvbiBtb2RlbHMgd2VyZSB0aGVuIGNyZWF0ZWQgZnJvbSB0aGUgY29tcGxldGVkIGRhdGEgdG8gcHJlZGljdCBtaXNzaW5nIHZhbHVlcyBmb3IgY2lnc1BlckRheSwgaGVhcnRSYXRlLCBCTUksIGFuZCBnbHVjb3NlLiAKClNpbmNlIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGlzIGxpbmVhcmx5IGNvcnJlbGF0ZWQgd2l0aCBCTUkgKEZpZ3VyZSBBKSwgYSBtb2RlbCB3YXMgY3JlYXRlZCB0byBwcmVkaWN0IHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgQk1JIHVzaW5nIHRoZSBsb2cgb2Ygc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgYXMgdGhlIHByZWRpY3Rvci4gCgpgYGB7cn0KCiMgU3RlcCAxOiBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIFN0ZXAgMjogR2VuZXJhdGUgYSAxMCUgYm9vdHN0cmFwIHNhbXBsZSBmcm9tIGhlYXJ0X2NsZWFuCnNhbXBsZV9zaXplIDwtIGZsb29yKDAuMSAqIG5yb3coaGVhcnRfY2xlYW4pKSAgIyAxMCUgb2YgdGhlIGRhdGEKYm9vdHN0cmFwX3NhbXBsZSA8LSBoZWFydF9jbGVhbltzYW1wbGUoMTpucm93KGhlYXJ0X2NsZWFuKSwgc2l6ZSA9IHNhbXBsZV9zaXplLCByZXBsYWNlID0gVFJVRSksIF0KCiMgU3RlcCAzOiBDcmVhdGUgYSBzY2F0dGVyIHBsb3QgZm9yIEJNSSBhbmQgc3lzQlAgaW4gdGhlIGJvb3RzdHJhcCBzYW1wbGUKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoYm9vdHN0cmFwX3NhbXBsZSwgYWVzKHggPSBzeXNCUCwgeSA9IEJNSSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgQk1JIHZzIHN5c3RvbGljIEJsb29kIFByZXNzdXJlICgxMCUgQm9vdHN0cmFwIFNhbXBsZSkiLAogICAgICAgeCA9ICJTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSIsCiAgICAgICB5ID0gIkJNSSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgUS1RIFBsb3QgZm9yIHN5c3RvbGljIEJQCmdncGxvdChib290c3RyYXBfc2FtcGxlLCBhZXMoc2FtcGxlID0gc3lzQlApKSArCiAgc3RhdF9xcSgpICsKICBzdGF0X3FxX2xpbmUoY29sID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlEtUSBQbG90IG9mIFN5c3RvbGljIEJsb29kIFByZXNzdXJlIChCb290c3RyYXAgU2FtcGxlKSIsCiAgICAgICB4ID0gIlRoZW9yZXRpY2FsIFF1YW50aWxlcyIsCiAgICAgICB5ID0gIlNhbXBsZSBRdWFudGlsZXMiKSArCiAgdGhlbWVfbWluaW1hbCgpCgojIFN0ZXAgMTogQnVpbGQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgbG9nKEJNSSkgYmFzZWQgb24gbG9nKHN5c0JQKQptb2RlbF9CTUkgPC0gbG0obG9nKEJNSSkgfiBsb2coc3lzQlApLCBkYXRhID0gaGVhcnRfY2xlYW4pCgojIFN0ZXAgMjogQ2hlY2sgdGhlIHN1bW1hcnkgb2YgdGhlIG1vZGVsCnN1bW1hcnkobW9kZWxfQk1JKQoKIyBTdGVwIDM6IElkZW50aWZ5IHJvd3Mgd2l0aCBtaXNzaW5nIEJNSSB2YWx1ZXMgaW4gaGVhcnRkYXRhCm1pc3NpbmdfQk1JX3Jvd3MgPC0gaXMubmEoaGVhcnRkYXRhJEJNSSkKCiMgU3RlcCA0OiBQcmVkaWN0IGxvZy10cmFuc2Zvcm1lZCBCTUkgdmFsdWVzIGZvciBtaXNzaW5nIHJvd3MKcHJlZGljdGVkX2xvZ19CTUkgPC0gcHJlZGljdChtb2RlbF9CTUksIG5ld2RhdGEgPSBoZWFydGRhdGFbbWlzc2luZ19CTUlfcm93cywgXSkKCiMgU3RlcCA1OiBFeHBvbmVudGlhdGUgdGhlIHByZWRpY3RlZCBsb2coQk1JKSB2YWx1ZXMgdG8gZ2V0IHByZWRpY3Rpb25zIGluIHRoZSBvcmlnaW5hbCBCTUkgc2NhbGUKcHJlZGljdGVkX0JNSSA8LSBleHAocHJlZGljdGVkX2xvZ19CTUkpCgojIFN0ZXAgNjogSW1wdXRlIHRoZSBwcmVkaWN0ZWQgQk1JIHZhbHVlcyBpbnRvIHRoZSBtaXNzaW5nIHBvc2l0aW9ucyBpbiBoZWFydGRhdGEKaGVhcnRkYXRhJEJNSVttaXNzaW5nX0JNSV9yb3dzXSA8LSBwcmVkaWN0ZWRfQk1JCgojIFN0ZXAgNzogQ2hlY2sgdGhlIGltcHV0ZWQgdmFsdWVzCnN1bW1hcnkoaGVhcnRkYXRhJEJNSSkKCgoKYGBgCkZpZ3VyZSBBLiBUaGUgYm9vdHN0cmFwIHNjYXR0ZXJwbG90IG9mIEJNSSBhbmQgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgZGlzcGxheXMgYSBtb2RlcmF0ZSBsaW5lYXIgcmVsYXRpb25zaGlwLiBUaGUgUVEgcGxvdCBpbmRpY2F0ZXMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGlzIG5vdCBub3JtYWwuIEhlbmNlLCB0aGUgbG9nIG9mIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGlzIHVzZWQgYXMgdGhlIHByZWRpY3Rvci4gCgoKR2x1Y29zZSBhbmQgQk1JIGFyZSBsaW5lYXJseSBjb3JyZWxhdGVkIChGaWd1cmUgQikuIFRoZXJlZm9yZSwgYSBtb2RlbCB1c2luZyBsb2cgb2YgQk1JIHRvIHByZWRpY3QgZ2x1Y29zZSB3YXMgY3JlYXRlZCB0byBpbXB1dGUgbWlzc2luZyBnbHVjb3NlIGRhdGEuIAoKYGBge3J9CgojUkVHUkVTU0lPTjogSU1QVVRFIE1JU1NJTkcgR0xVQ09TRSBVU0lORyBCTUkgQVMgQSBQUkVESUNUT1IKCiNCT09UU1RSQVAgU0FNUExFICYgU0NBVFRFUlBMT1QgRk9SIEdMVUNPU0UgQU5EIEJNSSBUTyBBU1NFU1MgTElORUFSIFJFTEFUSU9OU0hJUAoKIyBTdGVwIDE6IFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDU2NykKCiMgU3RlcCAyOiBHZW5lcmF0ZSBhIHNtYWxsZXIgYm9vdHN0cmFwIHNhbXBsZSAoZS5nLiwgMSUgb2YgdGhlIG9yaWdpbmFsIHNpemUpCnNhbXBsZV9zaXplIDwtIGZsb29yKDAuMSAqIG5yb3coaGVhcnRfY2xlYW4pKSAgCmJvb3RzdHJhcF9zYW1wbGVfc21hbGwgPC0gaGVhcnRfY2xlYW5bc2FtcGxlKDE6bnJvdyhoZWFydF9jbGVhbiksIHNpemUgPSBzYW1wbGVfc2l6ZSwgcmVwbGFjZSA9IFRSVUUpLCBdCgoKIyBTdGVwIDM6IENyZWF0ZSBhIHNjYXR0ZXJwbG90IGZvciB0aGUgYm9vdHN0cmFwIHNhbXBsZQpnZ3Bsb3QoYm9vdHN0cmFwX3NhbXBsZV9zbWFsbCwgYWVzKHggPSBCTUksIHkgPSBnbHVjb3NlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBvZiBHbHVjb3NlIHZzIEJNSSAoQm9vdHN0cmFwIFNhbXBsZSkiLAogICAgICAgeCA9ICJCTUkiLAogICAgICAgeSA9ICJHbHVjb3NlIikgKwogIHRoZW1lX21pbmltYWwoKQoKcXFub3JtKGJvb3RzdHJhcF9zYW1wbGVfc21hbGwkQk1JLCBtYWluID0gIlEtUSBQbG90IGZvciBCTUkgKEJvb3RzdHJhcCBTYW1wbGUpIikKcXFsaW5lKGJvb3RzdHJhcF9zYW1wbGVfc21hbGwkQk1JLCBjb2wgPSAicmVkIikKCgojIFN0ZXAgMTogUmVtb3ZlIGFueSBvYnNlcnZhdGlvbnMgd2l0aCBBTlkgbWlzc2luZyB2YWx1ZXMKaGVhcnRkYXRhX2NsZWFuMiA8LSBuYS5vbWl0KGhlYXJ0ZGF0YSkKCiMgU3RlcCAyOiBGaXQgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IGxvZyhnbHVjb3NlKSBiYXNlZCBvbiBsb2coQk1JKSB1c2luZyB0aGUgY2xlYW5lZCBkYXRhc2V0CmdsdWNvc2VfbW9kZWwgPC0gbG0obG9nKGdsdWNvc2UpIH4gbG9nKEJNSSksIGRhdGEgPSBoZWFydGRhdGFfY2xlYW4yKQoKIyBTdGVwIDM6IENoZWNrIHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbApzdW1tYXJ5KGdsdWNvc2VfbW9kZWwpCgojIFN0ZXAgNDogSWRlbnRpZnkgcm93cyB3aXRoIG1pc3NpbmcgZ2x1Y29zZSB2YWx1ZXMgaW4gaGVhcnRkYXRhCm1pc3NpbmdfZ2x1Y29zZV9yb3dzIDwtIGlzLm5hKGhlYXJ0ZGF0YSRnbHVjb3NlKQoKIyBTdGVwIDU6IFByZWRpY3QgbG9nLXRyYW5zZm9ybWVkIGdsdWNvc2UgdmFsdWVzIGZvciBtaXNzaW5nIHJvd3MKcHJlZGljdGVkX2xvZ19nbHVjb3NlIDwtIHByZWRpY3QoZ2x1Y29zZV9tb2RlbCwgbmV3ZGF0YSA9IGhlYXJ0ZGF0YVttaXNzaW5nX2dsdWNvc2Vfcm93cywgXSkKCiMgU3RlcCA2OiBFeHBvbmVudGlhdGUgdGhlIHByZWRpY3RlZCBsb2coZ2x1Y29zZSkgdmFsdWVzIHRvIGdldCBwcmVkaWN0aW9ucyBpbiB0aGUgb3JpZ2luYWwgc2NhbGUKcHJlZGljdGVkX2dsdWNvc2UgPC0gZXhwKHByZWRpY3RlZF9sb2dfZ2x1Y29zZSkKCiMgU3RlcCA3OiBJbXB1dGUgdGhlIHByZWRpY3RlZCBnbHVjb3NlIHZhbHVlcyBpbnRvIHRoZSBtaXNzaW5nIHBvc2l0aW9ucyBpbiBoZWFydGRhdGEKaGVhcnRkYXRhJGdsdWNvc2VbbWlzc2luZ19nbHVjb3NlX3Jvd3NdIDwtIHByZWRpY3RlZF9nbHVjb3NlCgojIFN0ZXAgODogQ2hlY2sgdGhlIGltcHV0ZWQgdmFsdWVzCnN1bW1hcnkoaGVhcnRkYXRhJGdsdWNvc2UpCgpgYGAKRmlndXJlIEIuIEEgbW9kZXJhdGUgcmVsYXRpb25zaGlwIGJldHdlZW4gQk1JIGFuZCBnbHVjb3NlIGlzIHZpc3VhbGl6ZWQgaW4gdGhlIHNjYXR0ZXJwbG90LiBTaW5jZSBCTUkgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChRUSBQbG90KSwgdGhlIGxvZyBvZiBCTUkgd2lsbCBiZSB1c2VkIGFzIGEgcHJlZGljdG9yIGZvciBnbHVjb3NlLiAKCgpTeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBpcyBtb2RlcmF0ZWx5IGNvcnJlbGF0ZWQgd2l0aCB0b3RhbCBjaG9sZXN0ZXJvbC4gVGhlcmVmb3JlLCBhIG1vZGVsIHRvIHByZWRpY3QgdGhlIG1pc3NpbmcgdG90YWwgY2hvbGVzdGVyb2wgdmFsdWVzIHdhcyBjcmVhdGVkIHVzaW5nIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGFzIGEgcHJlZGljdG9yLiAKCgpgYGB7cn0KCiMgU3RlcCAxOiBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIFN0ZXAgMjogR2VuZXJhdGUgYSAxMCUgYm9vdHN0cmFwIHNhbXBsZSBmcm9tIGhlYXJ0X2NsZWFuCnNhbXBsZV9zaXplIDwtIGZsb29yKDAuMSAqIG5yb3coaGVhcnRfY2xlYW4pKSAgIyAxMCUgb2YgdGhlIGRhdGEKYm9vdHN0cmFwX3NhbXBsZSA8LSBoZWFydF9jbGVhbltzYW1wbGUoMTpucm93KGhlYXJ0X2NsZWFuKSwgc2l6ZSA9IHNhbXBsZV9zaXplLCByZXBsYWNlID0gVFJVRSksIF0KCiMgU3RlcCAzOiBDcmVhdGUgYSBzY2F0dGVyIHBsb3QgZm9yIHRvdENob2wgYW5kIHN5c0JQIGluIHRoZSBib290c3RyYXAgc2FtcGxlCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGJvb3RzdHJhcF9zYW1wbGUsIGFlcyh4ID0gdG90Q2hvbCwgeSA9IHN5c0JQKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBvZiBUb3RhbCBDaG9sZXN0ZXJvbCB2cyBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoMTAlIEJvb3RzdHJhcCBTYW1wbGUpIiwKICAgICAgIHggPSAiVG90YWwgQ2hvbGVzdGVyb2wiLAogICAgICAgeSA9ICJTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSIpICsKICB0aGVtZV9taW5pbWFsKCkKCgojIFN0ZXAgMTogQnVpbGQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGxvZyhzeXNCUCkgdG8gcHJlZGljdCB0b3RDaG9sCm1vZGVsX3RvdENob2wgPC0gbG0obG9nKHRvdENob2wpIH4gbG9nKHN5c0JQKSwgZGF0YSA9IGhlYXJ0X2NsZWFuKQoKIyBTdGVwIDI6IENoZWNrIHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbApzdW1tYXJ5KG1vZGVsX3RvdENob2wpCgojIFN0ZXAgMzogSWRlbnRpZnkgcm93cyB3aXRoIG1pc3NpbmcgdG90Q2hvbCB2YWx1ZXMgaW4gaGVhcnRkYXRhCm1pc3NpbmdfdG90Q2hvbF9yb3dzIDwtIGlzLm5hKGhlYXJ0ZGF0YSR0b3RDaG9sKQoKIyBTdGVwIDQ6IFByZWRpY3QgbG9nLXRyYW5zZm9ybWVkIHRvdENob2wgdmFsdWVzIGZvciBtaXNzaW5nIHJvd3MKcHJlZGljdGVkX2xvZ190b3RDaG9sIDwtIHByZWRpY3QobW9kZWxfdG90Q2hvbCwgbmV3ZGF0YSA9IGhlYXJ0ZGF0YVttaXNzaW5nX3RvdENob2xfcm93cywgXSkKCiMgU3RlcCA1OiBFeHBvbmVudGlhdGUgdGhlIHByZWRpY3RlZCBsb2codG90Q2hvbCkgdmFsdWVzIHRvIGdldCBwcmVkaWN0aW9ucyBpbiB0aGUgb3JpZ2luYWwgc2NhbGUKcHJlZGljdGVkX3RvdENob2wgPC0gZXhwKHByZWRpY3RlZF9sb2dfdG90Q2hvbCkKCiMgU3RlcCA2OiBJbXB1dGUgdGhlIHByZWRpY3RlZCB0b3RDaG9sIHZhbHVlcyBpbnRvIHRoZSBtaXNzaW5nIHBvc2l0aW9ucyBpbiBoZWFydGRhdGEKaGVhcnRkYXRhJHRvdENob2xbbWlzc2luZ190b3RDaG9sX3Jvd3NdIDwtIHByZWRpY3RlZF90b3RDaG9sCgojIFN0ZXAgNzogQ2hlY2sgdGhlIGltcHV0ZWQgdmFsdWVzCnN1bW1hcnkoaGVhcnRkYXRhJHRvdENob2wpCgoKCgpgYGAKRmlndXJlIEMuIFRoZSBzY2F0dGVycGxvdCBkaXNwbGF5cyBhIHNsaWdodCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGFuZCB0b3RhbCBjaG9sZXN0ZXJvbC4gU3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChGaWd1cmUgQSkKCgpGb3VydGgsIGNpZ2FyZXR0ZXMgcGVyIGRheSBoYWQgMjkgbWlzc2luZyB2YWx1ZXMuIE9mIHRoZSAyOSBtaXNzaW5nIHZhbHVlcywgYWxsIGluZGl2aWR1YWxzIHdlcmUgY3VycmVudCBzbW9rZXJzLiBUaGUgbWVhbiBudW1iZXIgb2YgY2lnYXJldHRlcyBwZXIgZGF5IHdhcyBpbXB1dGVkIGZvciB0aGVzZSBtaXNzaW5nIHZhbHVlcy4gIAoKCmBgYHtyfQoKCiMgU3RlcCAxOiBDYWxjdWxhdGUgdGhlIG1lYW4gb2YgY2lnc1BlckRheSwgZXhjbHVkaW5nIG1pc3NpbmcgdmFsdWVzIChOQSkKbWVhbl9jaWdzUGVyRGF5IDwtIG1lYW4oaGVhcnRkYXRhJGNpZ3NQZXJEYXksIG5hLnJtID0gVFJVRSkKCiMgU3RlcCAyOiBJbXB1dGUgdGhlIG1pc3NpbmcgY2lnc1BlckRheSB2YWx1ZXMgd2l0aCB0aGUgY2FsY3VsYXRlZCBtZWFuCmhlYXJ0ZGF0YSRjaWdzUGVyRGF5W2lzLm5hKGhlYXJ0ZGF0YSRjaWdzUGVyRGF5KV0gPC0gbWVhbl9jaWdzUGVyRGF5CgoKYGBgCgpMYXN0LCB0aGUgZGF0YSBzdWJzZXQgc3RpbGwgaGFkIDEgbWlzc2luZyB2YWx1ZSBmb3IgaGVhcnRSYXRlLiBTaW5jZSB0aGUgZnJlcXVlbmN5IG9mIHRoaXMgc2luZ2xlIG1pc3NpbmcgdmFsdWUgaXMgdmVyeSBsb3csIHRoZSBtaXNzaW5nIHZhbHVlIHdhcyBpbXB1dGVkIHdpdGggdGhlIGF2ZXJhZ2UuIFRoZSBmaW5hbCBkYXRhIHNldCBoYWQgemVybyBtaXNzaW5nIHZhbHVlcy4gCgpgYGB7cn0KIyBTdGVwIDE6IEltcHV0ZSBtaXNzaW5nIGhlYXJ0UmF0ZSB2YWx1ZXMgd2l0aCB0aGUgYXZlcmFnZSBoZWFydFJhdGUKbWVhbl9oZWFydFJhdGUgPC0gbWVhbihoZWFydGRhdGEkaGVhcnRSYXRlLCBuYS5ybSA9IFRSVUUpCmhlYXJ0ZGF0YSRoZWFydFJhdGVbaXMubmEoaGVhcnRkYXRhJGhlYXJ0UmF0ZSldIDwtIG1lYW5faGVhcnRSYXRlCgoKYGBgCgojIEVEQQoKIyMgU2luZ2xlIFZhcmlhYmxlcwoKQWxsIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIHNldCB3ZXJlIHR5cGVkIGFzIG51bWVyaWNhbC4gSW4gdHVybiwgaW5kaWNhdG9yIHZhcmlhYmxlcyB3ZXJlIGNoYW5nZWQgdG8gZmFjdG9yLiBGb3IgdmlzdWFsaXphdGlvbiwgIm1hbGUiIHdhcyBjaGFuZ2VkIHRvICJzZXgiLiBUaGUgaW5kaWNhdG9yIHZhbHVlcyBmb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3ZXJlIGNoYW5nZWQgdG8gZGVzY3JpcHRpdmUgd29yZHMgb3IgcGhyYXNlcyB0byBlbmhhbmNlIHJlYWRhYmlsaXR5IG9mIHRoZSBncmFwaHMuIAoKCmBgYHtyfQoKIyBSZW5hbWUgaGVhcnRkYXRhdG8gaGVhcnR2aXogZm9yIHZpc3VhbGl6aW5nIGRhdGEKCmhlYXJ0dml6IDwtIGhlYXJ0ZGF0YQoKc3VtbWFyeShoZWFydHZpeikKCiMgUmVuYW1lIHRoZSB2YXJpYWJsZSAnbWFsZScgdG8gJ3NleCcKaGVhcnR2aXogPC0gaGVhcnRkYXRhICU+JQogIHJlbmFtZShzZXggPSBtYWxlKQoKI2NvbG5hbWVzKGhlYXJ0dml6KQoKIyBDaGVjayB0aGUgdW5pcXVlIHZhbHVlcyBpbiB0aGUgc2V4IGNvbHVtbgojdW5pcXVlKGhlYXJ0dml6JHNleCkKCiMgQ2hhbmdlIHZhbHVlcyBvZiBzZXggZnJvbSAxIGFuZCAwIHRvIG1hbGUgYW5kIGZlbWFsZQpoZWFydHZpeiRzZXggPC0gaWZlbHNlKGhlYXJ0dml6JHNleCA9PSAxLCAibWFsZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGhlYXJ0dml6JHNleCA9PSAwLCAiZmVtYWxlIiwgaGVhcnR2aXokc2V4KSkKCiNnbGltcHNlKGhlYXJ0dml6KQoKIyBDb3VudCBvY2N1cnJlbmNlcyBvZiBtYWxlcyBhbmQgZmVtYWxlcwpzZXhfY291bnQgPC0gdGFibGUoaGVhcnR2aXokc2V4KQoKIyBWaWV3IHRoZSBjb3VudAojcHJpbnQoc2V4X2NvdW50KQoKCmBgYAoKCgpgYGB7cn0KIyBDcmVhdGUgYSBiYXIgY2hhcnQgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoaGVhcnR2aXosIGFlcyh4ID0gc2V4KSkgKwogICBnZW9tX2JhcihhZXMoeSA9IChhZnRlcl9zdGF0KGNvdW50KSAvIHN1bShhZnRlcl9zdGF0KGNvdW50KSkpICogMTAwKSwgZmlsbCA9ICJsaWdodGJsdWUiKSArIAogIGxhYnModGl0bGUgPSAiU2V4IERpc3RyaWJ1dGlvbiIsIHggPSAiU2V4IiwgeSA9ICJQZXJjZW50IikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCkZpZ3VyZSBELiBUaGUgZGlzdHJpYnV0aW9uIG9mIHNleCBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBhcmUgbW9yZSBmZW1hbGUgZGF0YSB0aGFuIG1hbGUgZGF0YSB0aGF0IGV4aXN0cyBpbiB0aGlzIGRhdGEgc3Vic2V0LiBTcGVjaWZpY2FsbHksIHRoZXJlIGFyZSAyLDQyMCBmZW1hbGVzIGFuZCAxLDgyMCBtYWxlcyBpbmNsdWRlZCBpbiB0aGlzIGRhdGEgc2V0LiAKCgpgYGB7cn0KCiMgUmVjb2RlIGVkdWNhdGlvbiB2YXJpYWJsZSBhcyBhIGZhY3RvciB3aXRoIGxhYmVscwpoZWFydHZpeiA8LSBoZWFydHZpeiAlPiUKICBtdXRhdGUoZWR1Y2F0aW9uID0gZmFjdG9yKGVkdWNhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoMSwgMiwgMywgNCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJTb21lIEhTIiwgIkhTL0dFRCIsICJTb21lIENvbGxlZ2UvVm9jYXRpb25hbCBTY2hvb2wiLCAiQ29sbGVnZSIpKSkKCiMgRmlsdGVyIG91dCBhbnkgbWlzc2luZyB2YWx1ZXMgZm9yIGVkdWNhdGlvbgpoZWFydHZpeiA8LSBoZWFydHZpeiAlPiUgZmlsdGVyKCFpcy5uYShlZHVjYXRpb24pKQoKIyBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2UgZm9yIGVhY2ggZWR1Y2F0aW9uIGxldmVsCmVkdWNhdGlvbl9kaXN0IDwtIGhlYXJ0dml6ICU+JQogIGdyb3VwX2J5KGVkdWNhdGlvbikgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IChjb3VudCAvIHN1bShjb3VudCkpICogMTAwKQoKcHJpbnQoZWR1Y2F0aW9uX2Rpc3QpCgojIENyZWF0ZSB0aGUgYmFyIGNoYXJ0IHVzaW5nIHRoZSBwcmUtY2FsY3VsYXRlZCBwZXJjZW50YWdlcwpnZ3Bsb3QoZWR1Y2F0aW9uX2Rpc3QsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gcGVyY2VudGFnZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJnb2xkIikgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEVkdWNhdGlvbiBMZXZlbHMiLCAKICAgICAgIHggPSAiRWR1Y2F0aW9uIExldmVsIiwgCiAgICAgICB5ID0gIlBlcmNlbnRhZ2UiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKRmlndXJlIEUuIFRoZSBtb3N0IHByZXZhbGVudCBlZHVjYXRpb24gbGV2ZWwgZm9yIHRoZSBwYXJ0aWNpcGFudHMgaW4gdGhpcyBzdHVkeSBpcyBzb21lIGhpZ2ggc2Nob29sLCBmb2xsb3dlZCBieSBhIGhpZ2ggc2Nob29sIGRpcGxvbWEgb3IgR0VELiBUaGUgbGVhc3QgcHJldmFsZW50IGVkdWNhdGlvbiBhdHRhaW5tZW50IGlzIGNvbGxlZ2UgZGVncmVlLiAKCgoKCmBgYHtyfQoKIyBDaGFuZ2UgdmFsdWVzIG9mIGN1cnJlbnRTbW9rZXIKaGVhcnR2aXogPC0gaGVhcnR2aXogJT4lCiAgbXV0YXRlKGN1cnJlbnRTbW9rZXIgPSBpZmVsc2UoY3VycmVudFNtb2tlciA9PSAxLCAiWWVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShjdXJyZW50U21va2VyID09IDAsICJObyIsIGN1cnJlbnRTbW9rZXIpKSkKCiMgQ3JlYXRlIGEgYmFyIGNoYXJ0IHVzaW5nIGdncGxvdDIKZ2dwbG90KGhlYXJ0dml6LCBhZXMoeCA9IGN1cnJlbnRTbW9rZXIpKSArCiAgICBnZW9tX2JhcihhZXMoeSA9IChhZnRlcl9zdGF0KGNvdW50KSAvIHN1bShhZnRlcl9zdGF0KGNvdW50KSkpICogMTAwKSwgZmlsbCA9ICJvcmFuZ2UiKSArIAogIGxhYnModGl0bGUgPSAiQ3VycmVudCBTbW9rZXIiLCB4ID0gIlNtb2tlIiwgeSA9ICJQZXJjZW50IikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCkZpZ3VyZSBGLiBUaGUgcmVsYXRpdmUgZnJlcXVlbmNpZXMgb2Ygc21va2VycyBhbmQgbm9uLXNtb2tlcnMgYXJlIGFsbW9zdCB0aGUgc2FtZS4gVGhlcmUgd2VyZSAyLDE0NSBwYXJ0aWNpcGFudCB3aG8gd2VyZSBhY3RpdmUgc21va2Vycywgd2hpY2ggaXMgc2xpZ2h0bHkgbW9yZSB0aGFuIHRoZSAyLDA5NSBwYXJ0aWNpcGFudHMgd2hvIHdlcmUgbm90IGFjdGl2ZSBzbW9rZXJzLiAKCgoKYGBge3J9CgojIENoYW5nZSB2YWx1ZXMgb2YgQlBNZWRzCmhlYXJ0dml6IDwtIGhlYXJ0dml6ICU+JQogIG11dGF0ZShCUE1lZHMgPSBpZmVsc2UoQlBNZWRzID09IDEsICJZZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEJQTWVkcyA9PSAwLCAiTm8iLCBCUE1lZHMpKSkKCmhlYXJ0dml6IDwtIGhlYXJ0dml6WyFpcy5uYShoZWFydHZpeiRCUE1lZHMpLCBdICAKCiMgQ3JlYXRlIGEgYmFyIGNoYXJ0IHVzaW5nIGdncGxvdDIKZ2dwbG90KGhlYXJ0dml6LCBhZXMoeCA9IEJQTWVkcykpICsKICAgIGdlb21fYmFyKGFlcyh5ID0gKGFmdGVyX3N0YXQoY291bnQpIC8gc3VtKGFmdGVyX3N0YXQoY291bnQpKSkgKiAxMDApLCBmaWxsID0gImRhcmtncmVlbiIpICsgCiAgbGFicyh0aXRsZSA9ICJCbG9vZCBQcmVzc3VyZSBNZWRpY2F0aW9uIiwgeCA9ICJCUCBNZWRpY2F0aW9uIiwgeSA9ICJQZXJjZW50IikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCkZpZ3VyZSBHLiBBYm91dCA5NyUgb2YgcGFydGljaXBhbnRzIHdlcmUgbm90IG9uIGJsb29kIHByZXNzdXJlIG1lZGljYXRpb24gYXQgdGhlIHRpbWUgb2YgdGhpcyBzdHVkeS4gCgoKCmBgYHtyfQpoZWFydHZpeiA8LSBoZWFydHZpeiAlPiUKICBtdXRhdGUocHJldmFsZW50U3Ryb2tlID0gaWZlbHNlKHByZXZhbGVudFN0cm9rZSA9PSAxLCAiWWVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwcmV2YWxlbnRTdHJva2UgPT0gMCwgIk5vIiwgcHJldmFsZW50U3Ryb2tlKSkpCiAgCgojIENyZWF0ZSBhIGJhciBjaGFydCB1c2luZyBnZ3Bsb3QyCmdncGxvdChoZWFydHZpeiwgYWVzKHggPSBwcmV2YWxlbnRTdHJva2UpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSAoYWZ0ZXJfc3RhdChjb3VudCkgLyBzdW0oYWZ0ZXJfc3RhdChjb3VudCkpKSAqIDEwMCksIGZpbGwgPSAidmlvbGV0IikgKyAKICBsYWJzKHRpdGxlID0gIlByZXZhbGVudCBTdHJva2UiLCB4ID0gIlByZXZhbGVudCBTdHJva2UiLCB5ID0gIlBlcmNlbnQiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKRmlndXJlIEguIFZlcnkgZmV3IHBhcnRpY2lwYW50cywgbGVzcyB0aGFuIDAuNiUsIGhhZCBhIHN0cm9rZS4gCgpgYGB7cn0KaGVhcnR2aXogPC0gaGVhcnR2aXogJT4lCiAgbXV0YXRlKHByZXZhbGVudEh5cCA9IGlmZWxzZShwcmV2YWxlbnRIeXAgPT0gMSwgIlllcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocHJldmFsZW50SHlwID09IDAsICJObyIsIHByZXZhbGVudEh5cCkpKQogIAoKIyBDcmVhdGUgYSBiYXIgY2hhcnQgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoaGVhcnR2aXosIGFlcyh4ID0gcHJldmFsZW50SHlwKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gKGFmdGVyX3N0YXQoY291bnQpIC8gc3VtKGFmdGVyX3N0YXQoY291bnQpKSkgKiAxMDApLCBmaWxsID0gInJveWFsYmx1ZSIpICsgCiAgbGFicyh0aXRsZSA9ICJQcmV2YWxlbnQgSHlwZXJ0ZW5zaXZlIiwgeCA9ICJQcmV2YWxlbnQgSHlwZXJ0ZW5zaXZlIiwgeSA9ICJQZXJjZW50IikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCkZpZ3VyZSBJLiBBYm91dCAzMSUgb2YgcGFydGljaXBhbnRzIGhhZCByZWNlaXZlZCB0cmVhdG1lbnQgZm9yIGh5cGVydGVuc2lvbi4KCmBgYHtyfQpoZWFydHZpeiA8LSBoZWFydHZpeiAlPiUKICBtdXRhdGUoZGlhYmV0ZXMgPSBpZmVsc2UoZGlhYmV0ZXMgPT0gMSwgIlllcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGlhYmV0ZXMgPT0gMCwgIk5vIiwgZGlhYmV0ZXMpKSkKICAKCiMgQ3JlYXRlIGEgYmFyIGNoYXJ0IHVzaW5nIGdncGxvdDIKZ2dwbG90KGhlYXJ0dml6LCBhZXMoeCA9IGRpYWJldGVzKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gKGFmdGVyX3N0YXQoY291bnQpIC8gc3VtKGFmdGVyX3N0YXQoY291bnQpKSkgKiAxMDApLCBmaWxsID0gImJyb3duIikgKyAKICBsYWJzKHRpdGxlID0gIkRpYWJldGVzIiwgeCA9ICJEaWFiZXRlcyIsIHkgPSAiUGVyY2VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKCmBgYApGaWd1cmUgSi4gRGlhYmV0ZXMgd2FzIGluZnJlcXVlbnQsIGFib3V0IDIuNiUsIGFtb25nIHRoZSBwYXJ0aWNpcGFudHMgb2YgdGhpcyBzdHVkeS4KCgpgYGB7cn0KCiNIaXN0b2dyYW0gZm9yIGFnZQpnZ3Bsb3QoaGVhcnR2aXosIGFlcyh4ID0gYWdlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYmlud2lkdGggPSAxLCBmaWxsID0gImJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC41KSArIAogIGdlb21fZGVuc2l0eShjb2xvciA9ICJyZWQiLCBzaXplID0gMSkgKyAKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBBZ2UiLCAKICAgICAgIHggPSAiQWdlIiwgCiAgICAgICB5ID0gIkRlbnNpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYApGaWd1cmUgSy4gVGhlIGFnZSBkaXN0cmlidXRpb24gdmFyaWVzIGZyb20gc2xpZ2h0bHkgb3ZlciAzMCB5ZWFycyBvZiBhZ2UgdG8gYWJvdXQgNzAgeWVhcnMgb2xkLiBBIHNsaWdodCBza2V3bmVzcyB0byB0aGUgcmlnaHQgaXMgYXBwYXJlbnQuIFRoZSB0eXBpY2FsIGFnZSBvZiBhIHBhcnRpY2lwYW50IGlzIGFib3V0IDUwIHllYXJzIG9sZC4gCgoKCmBgYHtyfQoKCiMgQ3JlYXRlIGEgY29weSBvZiB0aGUgaGVhcnRkYXRhIGRhdGEgc2V0CmhlYXJ0ZGF0YUNIRCA8LSBoZWFydGRhdGEKIyBSZWNvZGUgVGVuWWVhckNIRCBpbiBoZWFydGRhdGFDSEQKaGVhcnRkYXRhQ0hEJFRlblllYXJDSEQgPC0gaWZlbHNlKGhlYXJ0ZGF0YUNIRCRUZW5ZZWFyQ0hEID09IDEsICJZZXMiLCAiTm8iKQoKIyBDcmVhdGUgdGhlIGJhciBjaGFydApnZ3Bsb3QoaGVhcnRkYXRhQ0hELCBhZXMoeCA9IFRlblllYXJDSEQpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pKSwgZmlsbCA9ICJmb3Jlc3RncmVlbiIsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBwYXN0ZTAocm91bmQoeCAqIDEwMCwgMSksICIlIikpICsKICBsYWJzKHRpdGxlID0gIlRlbi1ZZWFyIENvcm9uYXJ5IEhlYXJ0IERpc2Vhc2UgKENIRCkgT3V0Y29tZXMiLAogICAgICAgeCA9ICJUZW4tWWVhciBDSEQiLCB5ID0gIlBlcmNlbnRhZ2UiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKRmlndXJlIEwuIExlc3MgdGhhbiAyMCUgb2YgcGFydGljaXBhbnRzIGRldmVsb3BlZCBDSEQgd2l0aGluIHRlbiB5ZWFycyBvZiB0aGVpciBpbml0aWFsIGV4YW1pbmF0aW9uLiAKCgpgYGB7cn0KIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3QgZm9yIGNpZ3NQZXJEYXkgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBjaWdzUGVyRGF5KSkgKyAKICBnZW9tX2RlbnNpdHkobmEucm0gPSBUUlVFLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKyAgIyBGaWxsIGNvbG9yIGFuZCB0cmFuc3BhcmVuY3kKICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgQ3VydmUgZm9yIENpZ2FyZXR0ZXMgUGVyIERheSIsIAogICAgICAgeCA9ICJDaWdhcmV0dGVzIFBlciBEYXkiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIpCgpgYGAKRmlndXJlIE0uIFRoZSBkZW5zaXR5IGN1cnZlIGZvciB0aGUgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgcGVyIGRheSBpcyBtdWx0aS1tb2RhbC4gVGhlIGRpc3RyaWJ1dGlvbiBoYXMgYW4gb3ZlcmFsbCBza2V3ZWQgcmlnaHQgc2hhcGUuIFRoZSBtZWFuIG51bWJlciBvZiBjaWdhcmV0dGVzIHBlciBkYXkgaXMgbmluZSwgd2hpY2ggaXMgZ3JlYXRlciB0aGFuIHRoZSBtZWRpYW4gbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgcGVyIGRheSBvZiB6ZXJvLiBUaGUgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgcGVyIGRheSB2YXJpZXMgZnJvbSAwIHRvIDcwIGFuZCBoYXMgYW4gSVFSIG9mIDIwLgoKCmBgYHtyfQoKIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3QgZm9yIHRvdENob2wgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSB0b3RDaG9sKSkgKyAKICBnZW9tX2RlbnNpdHkobmEucm0gPSBUUlVFLCBmaWxsID0gInB1cnBsZSIsIGFscGhhID0gMC41KSArICAjIEZpbGwgY29sb3IgYW5kIHRyYW5zcGFyZW5jeQogIGxhYnModGl0bGUgPSAiMS4gRGVuc2l0eSBDdXJ2ZSBmb3IgVG90YWwgQ2hvbGVzdGVyb2wiLCAKICAgICAgIHggPSAiQ2hvbGVzdGVyb2wiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIpCgojIENhbGN1bGF0ZSB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRvdENob2wKbWVhbl90b3RDaG9sIDwtIG1lYW4oaGVhcnRkYXRhJHRvdENob2wsIG5hLnJtID0gVFJVRSkKc2RfdG90Q2hvbCA8LSBzZChoZWFydGRhdGEkdG90Q2hvbCwgbmEucm0gPSBUUlVFKQoKIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3QgZm9yIHRvdENob2wgYW5kIG92ZXJsYXkgYSBub3JtYWwgY3VydmUKZ2dwbG90KGRhdGEgPSBoZWFydGRhdGEsIGFlcyh4ID0gdG90Q2hvbCkpICsgCiAgZ2VvbV9kZW5zaXR5KG5hLnJtID0gVFJVRSwgZmlsbCA9ICJwdXJwbGUiLCBhbHBoYSA9IDAuNSkgKyAgIyBEZW5zaXR5IGN1cnZlCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW5fdG90Q2hvbCwgc2QgPSBzZF90b3RDaG9sKSwgCiAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKyAgIyBOb3JtYWwgY3VydmUKICBsYWJzKHRpdGxlID0gIjIuIERlbnNpdHkgQ3VydmUgZm9yIFRvdGFsIENob2xlc3Rlcm9sIHdpdGggTm9ybWFsIEN1cnZlIiwgCiAgICAgICB4ID0gIkNob2xlc3Rlcm9sIiwgCiAgICAgICB5ID0gIkRlbnNpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpCgojIFBlcmZvcm0gU2hhcGlyby1XaWxrIHRlc3QgZm9yIG5vcm1hbGl0eQpzaGFwaXJvX3Rlc3RfcmVzdWx0IDwtIHNoYXBpcm8udGVzdChoZWFydGRhdGEkdG90Q2hvbCkKCiMgUHJpbnQgdGhlIHRlc3QgcmVzdWx0CnNoYXBpcm9fdGVzdF9yZXN1bHQKCiMgQ3JlYXRlIGEgYm94IHBsb3QgZm9yIHRoZSB0b3RDaG9sIHZhcmlhYmxlCmdncGxvdChkYXRhID0gaGVhcnRkYXRhLCBhZXMoeCA9ICIiLCB5ID0gdG90Q2hvbCkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDE2LCBvdXRsaWVyLnNpemUgPSAyKSArICAjIEJveCBwbG90IHdpdGggY3VzdG9tIG91dGxpZXJzCiAgbGFicyh0aXRsZSA9ICIzLiBCb3ggUGxvdCBvZiBUb3RhbCBDaG9sZXN0ZXJvbCIsIAogICAgICAgeCA9ICIiLCAKICAgICAgIHkgPSAiQ2hvbGVzdGVyb2wiKSArCiAgdGhlbWVfbWluaW1hbCgpCgoKIyBDYWxjdWxhdGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0b3RDaG9sLCBpZ25vcmluZyBtaXNzaW5nIHZhbHVlcwpzZF90b3RDaG9sIDwtIHNkKGhlYXJ0ZGF0YSR0b3RDaG9sLCBuYS5ybSA9IFRSVUUpCgojIFByaW50IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24Kc2RfdG90Q2hvbAoKIyBGaXQgYSBsaW5lYXIgbW9kZWwKbW9kZWwgPC0gbG0odG90Q2hvbCB+IGFnZSArIEJNSSArIGhlYXJ0UmF0ZSwgZGF0YSA9IGhlYXJ0ZGF0YSkKCiMgQ2FsY3VsYXRlIHRoZSBKYWNra25pZmUgcmVzaWR1YWxzCmphY2trbmlmZV9yZXNpZHVhbHMgPC0gcnN0dWRlbnQobW9kZWwpICAjIFN0dWRlbnRpemVkIHJlc2lkdWFscyBhcmUgdXNlZCB0byBpZGVudGlmeSBvdXRsaWVycwoKIyBBZGQgSmFja2tuaWZlIHJlc2lkdWFscyB0byB0aGUgZGF0YSBzZXQKaGVhcnRkYXRhJGphY2trbmlmZV9yZXNpZHVhbHMgPC0gamFja2tuaWZlX3Jlc2lkdWFscwoKIyBJZGVudGlmeSBwb3RlbnRpYWwgb3V0bGllcnMgd2hlcmUgdGhlIGFic29sdXRlIHZhbHVlIG9mIEphY2trbmlmZSByZXNpZHVhbHMgPiAyCm91dGxpZXJzIDwtIGhlYXJ0ZGF0YVthYnMoaGVhcnRkYXRhJGphY2trbmlmZV9yZXNpZHVhbHMpID4gMiwgXQoKIyBEaXNwbGF5IHBvdGVudGlhbCBvdXRsaWVycwojb3V0bGllcnMKYGBgCkZpZ3VyZSBOLiAKCjEuIFRoZSB0b3RhbCBjaG9sZXN0ZXJvbCBkaXN0cmlidXRpb24gYXBwZWFycyB0byBiZSByb3VnaGx5IHN5bW1ldHJpYyB3aXRoIGV4dHJlbWUgbG93IGFuZCBoaWdoIHZhbHVlcy4gVGhlIG1lYW4gdG90YWwgY2hvbGVzdGVyb2wgaXMgMjM2LjcgbWcvZEwgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiA0NC4zIG1nL2RMLiBUaGUgbWVkaWFuIHRvdGFsIGNob2xlc3Rlcm9sIGlzIDIzNCBtZy9kTCwgd2hpY2ggaXMgc2xpZ2h0bHkgbGVzcyB0aGFuIHRoZSBtZWFuIGluZGljYXRpbmcgYSBzbGlnaHRseSBza2V3ZWQgcmlnaHQgZGlzdHJpYnV0aW9uLiBUaGUgZGF0YSB2YXJpZXMgZnJvbSAxMDcgbWcvZEwgdG8gNjk2IG1nL2RMLiAKCjIuIEFsdGhvdWdoIHRoZSBkaXN0cmlidXRpb24gaGFzIGEgc2xpZ2h0IHNrZXduZXNzIHRvIHRoZSByaWdodCwgdGhlIGRpc3RyaWJ1dGlvbiBhcHBlYXJzIHRvIGZpdCB0aGUgTm9ybWFsIGN1cnZlIG1vZGVyYXRlbHkgd2VsbC4gSG93ZXZlciwgdGhlIFNoYXBpcm8tV2lsayB0ZXN0IHJlc3VsdGVkIGluIGEgcC12YWx1ZSBvZiA8IDIuMmUtMTYsIHdoaWNoIHJlamVjdHMgdGhlIG51bGwgaHlwb3RoZXNpcywgaW5kaWNhdGluZyB0aGF0IHRoZXJlIGlzIG5vdCBjb252aW5jaW5nIGV2aWRlbmNlIG9mIGEgTm9ybWFsIGRpc3RyaWJ1dGlvbiBmb3IgdG90YWwgY2hvbGVzdGVyb2wuCgozLiBBbiBleGFtaW5hdGlvbiBvZiBhIGJveCBwbG90IGluZGljYXRlcyBsb3cgYW5kIGhpZ2ggb3V0bGllcnMuIFRoZSByZXN1bHRzIG9mIGEgamFja2tuaWZlIHJlc2lkdWFsIGFuYWx5c2lzIGluZGljYXRlcyAxNzMgb3V0bGllcnMuIFRoZSBtYWpvcml0eSBvZiBvdXRsaWVycyBhcmUgaGlnaCBvdXRsaWVycy4gCgoKCmBgYHtyfQojIENyZWF0ZSBhIGRlbnNpdHkgcGxvdCBmb3Igc3lzQlAgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBzeXNCUCkpICsgCiAgZ2VvbV9kZW5zaXR5KG5hLnJtID0gVFJVRSwgZmlsbCA9ICJnb2xkIiwgYWxwaGEgPSAwLjUpICsgICMgRmlsbCBjb2xvciBhbmQgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IEN1cnZlIGZvciBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSIsIAogICAgICAgeCA9ICJTeXN0b2xpYyBCUCIsIAogICAgICAgeSA9ICJEZW5zaXR5IikKCiMgQ3JlYXRlIGEgYm94IHBsb3QgZm9yIHN5c0JQCmdncGxvdChkYXRhID0gaGVhcnRkYXRhLCBhZXMoeCA9ICIiLCB5ID0gc3lzQlApKSArIAogIGdlb21fYm94cGxvdChmaWxsID0gIndoaXRlIiwgb3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDE2LCBvdXRsaWVyLnNpemUgPSAyKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoc3lzQlApIiwgCiAgICAgICB5ID0gIlN5c3RvbGljIEJsb29kIFByZXNzdXJlIChzeXNCUCkiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYApGaWd1cmUgTy4gU3lzdG9saWMgQmxvb2QgUHJlc3N1cmUgZGlzdHJpYnV0aW9uIGlzIHNrZXdlZCByaWdodC4gVGhlIG1lZGlhbiBpcyAxMjggbW1IZyB3aXRoIGFuIElRUiBvZiAyNyBtbUhnLiBTZXZlcmFsIGhpZ2ggb3V0bGllcnMgZXhpc3QgaW4gdGhpcyBkaXN0cmlidXRpb24uIFRoZSBoaWdoZXN0IHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGlzIDI5NSBtbUhnLgoKCmBgYHtyfQojIENyZWF0ZSBhIGRlbnNpdHkgcGxvdCBmb3IgZGlhQlAgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBkaWFCUCkpICsgCiAgZ2VvbV9kZW5zaXR5KG5hLnJtID0gVFJVRSwgZmlsbCA9ICJ5ZWxsb3ciLCBhbHBoYSA9IDAuNSkgKyAgIyBGaWxsIGNvbG9yIGFuZCB0cmFuc3BhcmVuY3kKICBsYWJzKHRpdGxlID0gIjEuIERlbnNpdHkgQ3VydmUgZm9yIERpYXN0b2xpYyBCbG9vZCBQcmVzc3VyZSIsIAogICAgICAgeCA9ICJEaWFzdG9saWMgQlAiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIpCgoKIyBDYWxjdWxhdGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBub3JtYWwgY3VydmUKbWVhbl9kaWFCUCA8LSBtZWFuKGhlYXJ0ZGF0YSRkaWFCUCwgbmEucm0gPSBUUlVFKQpzZF9kaWFCUCA8LSBzZChoZWFydGRhdGEkZGlhQlAsIG5hLnJtID0gVFJVRSkKCgojIENyZWF0ZSBoaXN0b2dyYW0gd2l0aCBub3JtYWwgY3VydmUgb3ZlcmxheQpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBkaWFCUCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGJpbnMgPSAzMCwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43KSArIAogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuX2RpYUJQLCBzZCA9IHNkX2RpYUJQKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlID0gIjIuIEhpc3RvZ3JhbSBvZiBEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUgKGRpYUJQKSB3aXRoIE5vcm1hbCBDdXJ2ZSIsIAogICAgICAgeCA9ICJEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUgKGRpYUJQKSIsIAogICAgICAgeSA9ICJEZW5zaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDcmVhdGUgYSBib3ggcGxvdCBmb3IgZGlhQlAKZ2dwbG90KGRhdGEgPSBoZWFydGRhdGEsIGFlcyh4ID0gIiIsIHkgPSBkaWFCUCkpICsgCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgb3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDE2LCBvdXRsaWVyLnNpemUgPSAyKSArCiAgbGFicyh0aXRsZSA9ICIzLiBCb3ggUGxvdCBvZiBEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUgKGRpYUJQKSIsIAogICAgICAgeSA9ICJEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUgKGRpYUJQKSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgUGVyZm9ybSBTaGFwaXJvLVdpbGsgbm9ybWFsaXR5IHRlc3QgZm9yIGRpYUJQCnNoYXBpcm9fdGVzdF9yZXN1bHQgPC0gc2hhcGlyby50ZXN0KGhlYXJ0ZGF0YSRkaWFCUCkKCiMgUHJpbnQgdGhlIHJlc3VsdHMKI3ByaW50KHNoYXBpcm9fdGVzdF9yZXN1bHQpCgojIENyZWF0ZSBhIFEtUSBwbG90IGZvciBkaWFCUApxcW5vcm0oaGVhcnRkYXRhJGRpYUJQLCBtYWluID0gIjQuIFEtUSBQbG90IG9mIERpYXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoZGlhQlApIikKcXFsaW5lKGhlYXJ0ZGF0YSRkaWFCUCwgY29sID0gInJlZCIpICAjIEFkZCBhIHJlZmVyZW5jZSBsaW5lCgoKYGBgCkZpZ3VyZSBQLiAKCjEuIFRoZSBkZW5zaXR5IGN1cnZlIGZvciB0aGUgZGlhc3RvbGljIEJQIGRpc3RyaWJ1dGlvbiBoYXMgYSByb3VnaGx5IHN5bW1ldHJpYy4gVGhlIG1lYW4gb2YgODIuOSBtbUhnIGlzIHNpbWlsYXIgdG8gdGhlIG1lZGlhbiBvZiA4MiBtbUhnLCBjb25maXJtaW5nIGEgcm91Z2hseSBzeW1tZXRyaWMgZGlzdHJpYnV0aW9uLiBUaGUgZGlhc3RvbGljIEJQIGRhdGEgdmFyaWVzIGZyb20gNDggdG8gMTQyLjUgbW1IZy4gCgoyLiBBIE5vcm1hbCBjdXJ2ZSBzdXBlcmltcG9zZWQgb3ZlciBhIGhpc3RvZ3JhbSBpbmRpY2F0ZXMgdGhhdCB0aGUgRGlhc3RvbGljIEJQIGRpc3RyaWJ1dGlvbiBtYXkgYmUgTm9ybWFsbHkgZGlzdHJpYnV0ZWQuIEhvd2V2ZXIsIGEgU2hhcGlyby1XaWxrIG5vcm1hbGl0eSB0ZXN0IGluZGljYXRlcyB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgbm90IG5vcm1hbC4gCgozLiBCb3RoIGxvdyBhbmQgaGlnaCBvdXRsaWVycyBjYW4gYmUgc2VlbiBpbiB0aGUgYm94IHBsb3QuIE1vc3Qgb2YgdGhlIG9ic2VydmF0aW9ucyB0aGF0IGFyZSBvdXRsaWVycyBhcmUgaGlnaC4gCgo0LiBUaGUgUS1RIHBsb3QgaGFzIGEgY29uY2F2ZSB1cCBmb3JtIHdoaWNoIGNvbmZpcm1zIHRoYXQgdGhlIERpYXN0b2xpYyBCUCBkaXN0cmlidXRpb24gaXMgbm90IG5vcm1hbC4gCgoKYGBge3J9CiMgQ3JlYXRlIGEgZGVuc2l0eSBwbG90IGZvciBCTUkgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBCTUkpKSArIAogIGdlb21fZGVuc2l0eShuYS5ybSA9IFRSVUUsIGZpbGwgPSAicHVycGxlIiwgYWxwaGEgPSAwLjUpICsgICMgRmlsbCBjb2xvciBhbmQgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IEN1cnZlIGZvciBCTUkiLCAKICAgICAgIHggPSAiQk1JIiwgCiAgICAgICB5ID0gIkRlbnNpdHkiKQoKIyBDYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBCTUkKbWVhbl9CTUkgPC0gbWVhbihoZWFydGRhdGEkQk1JLCBuYS5ybSA9IFRSVUUpCnNkX0JNSSA8LSBzZChoZWFydGRhdGEkQk1JLCBuYS5ybSA9IFRSVUUpCgojIENyZWF0ZSBhIGhpc3RvZ3JhbSB3aXRoIGEgbm9ybWFsIGN1cnZlIHN1cGVyaW1wb3NlZApnZ3Bsb3QoaGVhcnRkYXRhLCBhZXMoeCA9IEJNSSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYmlud2lkdGggPSAxLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbl9CTUksIHNkID0gc2RfQk1JKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBCTUkgd2l0aCBOb3JtYWwgQ3VydmUiLCB4ID0gIkJNSSIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgQ3JlYXRlIGEgYm94IHBsb3QgZm9yIEJNSQpnZ3Bsb3QoaGVhcnRkYXRhLCBhZXMoeSA9IEJNSSkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJsaWdodGdyZWVuIiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBCTUkiLCB5ID0gIkJNSSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgQk1JCnNkX0JNSSA8LSBzZChoZWFydGRhdGEkQk1JLCBuYS5ybSA9IFRSVUUpCgojIERpc3BsYXkgdGhlIHJlc3VsdApzZF9CTUkKCiMgUGVyZm9ybSBTaGFwaXJvLVdpbGsgbm9ybWFsaXR5IHRlc3Qgb24gQk1JCnNoYXBpcm9fdGVzdF9CTUkgPC0gc2hhcGlyby50ZXN0KGhlYXJ0ZGF0YSRCTUkpCgojIFZpZXcgdGhlIHJlc3VsdAojc2hhcGlyb190ZXN0X0JNSQoKIyBDcmVhdGUgYSBRLVEgcGxvdCBmb3IgQk1JCnFxbm9ybShoZWFydGRhdGEkQk1JLCBtYWluID0gIlEtUSBQbG90IG9mIEJNSSIpCnFxbGluZShoZWFydGRhdGEkQk1JLCBjb2wgPSAicmVkIikgICMgQWRkIGEgcmVmZXJlbmNlIGxpbmUKCmBgYApGaWd1cmUgUS4gVGhlIEJNSSBkaXN0cmlidXRpb24gYXBwZWFycyB0byBoYXZlIGEgc2xpZ2h0bHkgc2tld2VkIHJpZ2h0IGRpc3RyaWJ1dGlvbiBkdWUgdG8gc29tZSBleHRyZW1lbHkgaGlnaCB2YWx1ZXMuIFdpdGhvdXQgdGhlc2Ugb3V0bGllcnMsIHRoZSBkYXRhIGFwcGVhcnMgcm91Z2hseSBzeW1tZXRyaWMuIFRoZSBkYXRhIHZhcmllcyBmcm9tIDE1LjU0IHRvIDU2LjgwIGtnL21eMi4gVGhlIG1lYW4gQk1JIGZvciB0aGlzIGRhdGEgc3Vic2V0IGlzIDI1Ljgga2cvbV4yLCB3aGljaCBpcyBzaW1pbGFyIHRvIHRoZSBtZWRpYW4gdmFsdWUgb2YgMjUuNCBrZy9tXjIuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgNC4wNyBrZy9tXjIuIAoKYGBge3J9CiMgQ3JlYXRlIGEgZGVuc2l0eSBwbG90IGZvciBoZWFydFJhdGUgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBoZWFydFJhdGUpKSArIAogIGdlb21fZGVuc2l0eShuYS5ybSA9IFRSVUUsIGZpbGwgPSAibGlnaHRibHVlIiwgYWxwaGEgPSAwLjUpICsgICMgRmlsbCBjb2xvciBhbmQgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IEN1cnZlIGZvciBIZWFydCBSYXRlIiwgCiAgICAgICB4ID0gIkhlYXJ0IFJhdGUiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIpCgojIENhbGN1bGF0ZSB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGhlYXJ0UmF0ZQptZWFuX2hlYXJ0UmF0ZSA8LSBtZWFuKGhlYXJ0ZGF0YSRoZWFydFJhdGUsIG5hLnJtID0gVFJVRSkKc2RfaGVhcnRSYXRlIDwtIHNkKGhlYXJ0ZGF0YSRoZWFydFJhdGUsIG5hLnJtID0gVFJVRSkKCiMgQ3JlYXRlIGEgaGlzdG9ncmFtIGZvciBoZWFydFJhdGUgd2l0aCBhIG5vcm1hbCBjdXJ2ZSBzdXBlcmltcG9zZWQKZ2dwbG90KGhlYXJ0ZGF0YSwgYWVzKHggPSBoZWFydFJhdGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGJpbndpZHRoID0gMSwgZmlsbCA9ICJzZWFzaGVsbCIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbl9oZWFydFJhdGUsIHNkID0gc2RfaGVhcnRSYXRlKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBIZWFydCBSYXRlIHdpdGggTm9ybWFsIEN1cnZlIiwgeCA9ICJIZWFydCBSYXRlIiwgeSA9ICJEZW5zaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDcmVhdGUgYSBib3ggcGxvdCBmb3IgaGVhcnRSYXRlCmdncGxvdChoZWFydGRhdGEsIGFlcyh5ID0gaGVhcnRSYXRlKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gImxhdmVuZGVyIiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBIZWFydCBSYXRlIiwgeSA9ICJIZWFydCBSYXRlIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDYWxjdWxhdGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBoZWFydFJhdGUKc2RfaGVhcnRSYXRlIDwtIHNkKGhlYXJ0ZGF0YSRoZWFydFJhdGUsIG5hLnJtID0gVFJVRSkKCiMgRGlzcGxheSB0aGUgcmVzdWx0CnNkX2hlYXJ0UmF0ZQoKIyBQZXJmb3JtIFNoYXBpcm8tV2lsayBub3JtYWxpdHkgdGVzdCBmb3IgaGVhcnRSYXRlCnNoYXBpcm9fdGVzdF9yZXN1bHQgPC0gc2hhcGlyby50ZXN0KGhlYXJ0ZGF0YSRoZWFydFJhdGUpCgojIERpc3BsYXkgdGhlIHJlc3VsdAojc2hhcGlyb190ZXN0X3Jlc3VsdAoKIyBDcmVhdGUgYSBRLVEgcGxvdCBmb3IgaGVhcnRSYXRlCnFxbm9ybShoZWFydGRhdGEkaGVhcnRSYXRlLCBtYWluID0gIlEtUSBQbG90IG9mIEhlYXJ0IFJhdGUiKQpxcWxpbmUoaGVhcnRkYXRhJGhlYXJ0UmF0ZSwgY29sID0gInJlZCIpICAjIEFkZCBhIHJlZmVyZW5jZSBsaW5lCgpgYGAKRmlndXJlIFIuIFRoZSBoZWFydFJhdGUgZGlzdHJpYnV0aW9uIGFwcGVhcnMgdG8gYmUgcm91Z2hseSBzeW1tZXRyaWMgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIGEgZmV3IGhpZ2ggb3V0bGllcnMuIFRoZSBtZWFuIGhlYXJ0IHJhdGUgaXMgNzUuODggYmVhdHMvbWluIHdoaWNoIGlzIGNsb3NlIHRvIHRoZSBtZWRpYW4gaGVhcnQgcmF0ZSBvZiA3NSBiZWF0cy9taW4uIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgMTIuMDIgYmVhdHMvbWluLiBUaGUgU2hhcGlybyBXaWxrIHRlc3QgZm9yIG5vcm1hbGl0eSBwcm9kdWNlcyBhIHRlc3Qgc3RhdGlzdGljIHZlcnkgY2xvc2UgdG8gMSwgaW5kaWNhdGluZyBub3JtYWxpdHkuIEhvd2V2ZXIsIHRoZSB0ZXN0IHdhcyBpbnNpZ25pZmljYW50LiBUaGUgUVEgcGxvdCBwb3NzaWJseSBpbmRpY2F0ZXMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIGZvbGxvd3MgYSB0LWRpc3RyaWJ1dGlvbi4gCgpgYGB7cn0KIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3QgZm9yIGdsdWNvc2UgdXNpbmcgZ2dwbG90MgpnZ3Bsb3QoZGF0YSA9IGhlYXJ0ZGF0YSwgYWVzKHggPSBnbHVjb3NlKSkgKyAKICBnZW9tX2RlbnNpdHkobmEucm0gPSBUUlVFLCBmaWxsID0gImxhdmVuZGVyIiwgYWxwaGEgPSAwLjUpICsgICMgRmlsbCBjb2xvciBhbmQgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IEN1cnZlIGZvciBHbHVjb3NlIiwgCiAgICAgICB4ID0gIkdsdWNvc2UiLCAKICAgICAgIHkgPSAiRGVuc2l0eSIpCgojIENyZWF0ZSBhIGJveCBwbG90IGZvciBnbHVjb3NlCmdncGxvdChkYXRhID0gaGVhcnRkYXRhLCBhZXMoeSA9IGdsdWNvc2UpKSArIAogIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0Ymx1ZSIsIG91dGxpZXIuY29sb3VyID0gInJlZCIsIG91dGxpZXIuc2l6ZSA9IDIpICsgCiAgbGFicyh0aXRsZSA9ICJCb3ggUGxvdCBvZiBHbHVjb3NlIExldmVscyIsIAogICAgICAgeSA9ICJHbHVjb3NlIExldmVsIikgKyAKICB0aGVtZV9taW5pbWFsKCkKCiMgQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZ2x1Y29zZQojc3RkX2Rldl9nbHVjb3NlIDwtIHNkKGhlYXJ0ZGF0YSRnbHVjb3NlLCBuYS5ybSA9IFRSVUUpICAjIG5hLnJtID0gVFJVRSBleGNsdWRlcyBhbnkgbWlzc2luZyB2YWx1ZXMKI3N0ZF9kZXZfZ2x1Y29zZQoKCmBgYApGaWd1cmUgUy4gVGhlIGdsdWNvc2UgZGlzdHJpYnV0aW9uIGlzIHNrZXdlZCByaWdodC4gVGhlIG1lZGlhbiBnbHVjb3NlIGxldmVsIGlzIDc5IG1nL2RMIHdoaWNoIGlzIHNsaWdodGx5IGxlc3MgdGhhbiB0aGUgbWVhbiBnbHVjb3NlIGxldmVsIG9mIDgxLjkzIG1nL2RMLiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGdsdWNvc2UgaXMgMjIuODUgbWcvZEwuIFRoZSBnbHVjb3NlIGxldmVscyB2YXJ5IGZyb20gNDAgdG8gMzk0IG1nL2RMLiBUaGUgbWFqb3JpdHkgb2YgZXh0cmVtZSB2YWx1ZXMgYXJlIGhpZ2ggb3V0bGllcnMgYW5kIGFib3V0IHRocmVlIHZhbHVlcyBhcmUgbG93IG91dGxpZXJzLiAKCgojIyBQYWlyd2lzZSBSZWxhdGlvbnNoaXBzCgpUaGUgcGFpcndpc2UgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBhcmUgZXhhbWluZWQuIFRoZSB2YXJpYWJsZXMgY29uc2lkZXJlZCBhcmUgbnVtZXJpY2FsLiAKCgpgYGB7cn0KCiMgU3RlcCAxOiBEZWZpbmUgdGhlIG9yaWdpbmFsIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMKcXVhbnRpdGF0aXZlX3ZhcnMgPC0gaGVhcnRkYXRhWywgYygiYWdlIiwgImNpZ3NQZXJEYXkiLCAidG90Q2hvbCIsICJzeXNCUCIsICJkaWFCUCIsICJCTUkiLCAiaGVhcnRSYXRlIiwgImdsdWNvc2UiKV0KCiMgU3RlcCAyOiBDb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSBzcGVjaWZpZWQgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcwpjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKHF1YW50aXRhdGl2ZV92YXJzLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCiMgU3RlcCAzOiBWaXN1YWxpemUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCB1c2luZyBjb3JycGxvdAojIEFkanVzdCBtYXJnaW5zIHRvIHByZXZlbnQgc3F1aXNoaW5nIGFuZCBjdXQtb2ZmIHRpdGxlCnBhcihtYXIgPSBjKDUsIDEsIDQsIDEpKSAgIyBib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQgbWFyZ2lucwoKIyBJbmNyZWFzZSB0aGUgc2l6ZSBvZiB0aGUgcGxvdCBieSBhZGp1c3RpbmcgdGhlIGxheW91dCBwYXJhbWV0ZXJzCmNvcnJwbG90KGNvcnJlbGF0aW9uX21hdHJpeCwgCiAgICAgICAgIG1ldGhvZCA9ICJjaXJjbGUiLCAgICAgICAgIyBDaG9vc2UgJ2NpcmNsZScgZm9yIGNpcmN1bGFyIHZpc3VhbGl6YXRpb24KICAgICAgICAgdHlwZSA9ICJsb3dlciIsICAgICAgICAgICAjIFNob3cgb25seSB0aGUgbG93ZXIgdHJpYW5nbGUgb2YgdGhlIG1hdHJpeAogICAgICAgICBhZGRDb2VmLmNvbCA9ICJibGFjayIsICAgICMgQWRkIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBpbiBibGFjawogICAgICAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBNYXRyaXggZm9yIE9yaWdpbmFsIFF1YW50aXRhdGl2ZSBWYXJpYWJsZXMiLAogICAgICAgICB0bC5jb2wgPSAiYmxhY2siLCAgICAgICAgICMgQ29sb3IgZm9yIHRoZSB0ZXh0IGxhYmVscwogICAgICAgICB0bC5zcnQgPSA0NSwgICAgICAgICAgICAgICMgUm90YXRlIHRleHQgbGFiZWxzIGZvciBiZXR0ZXIgcmVhZGFiaWxpdHkKICAgICAgICAgY2wubGltID0gYygtMSwgMSksICAgICAgICAjIFNldCBjb2xvciBsaW1pdHMKICAgICAgICAgbnVtYmVyLmNleCA9IDAuNywgICAgICAgICAjIEFkanVzdCBmb250IHNpemUgb2YgdGhlIG51bWJlcnMKICAgICAgICAgdGwuY2V4ID0gMC44LCAgICAgICAgICAgICAjIEFkanVzdCBmb250IHNpemUgb2YgdGhlIHRleHQgbGFiZWxzCiAgICAgICAgIGNleC5tYWluID0gMS4yKSAgICAgICAgICAgIyBBZGp1c3QgdGl0bGUgc2l6ZQpgYGAKCkZpZ3VyZSBULiBUaGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIGlzIGJldHdlZW4gc3lzdG9saWMgYW5kIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZS4gQWdlIGFuZCBTeXN0b2xpYyBCUCBoYXZlIGEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIG9mIDAuMzkuIEFzIGFnZSBpbmNyZWFzZXMsIHNvIGRvZXMgU3lzdG9saWMgQlAuIEJNSSBpcyBhbHNvIHBvc2l0aXZlbHkgcmVsYXRlZCB0byBTeXN0b2xpYyBhbmQgRGlhc3RvbGljIEJQLiBBcyBCTUkgaW5jcmVhc2VzLCBzbyBkb2VzIGJvdGggb2YgdGhlIEJQIG1lYXN1cmVzLiBUb3RhbCBjaG9sZXN0ZXJvbCBhbmQgYWdlIGhhdmUgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBvZiAwLjI2LiBNb3N0IG90aGVyIHBhaXJ3aXNlIHZhcmlhYmxlcyBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggY29ycmVsYXRpb24gY29lZmZpY2llbnRzIG9mIGxlc3MgdGhhbiAwLjIwLiBUaGVyZSBhcmUganVzdCBhIGZldyBwYWlyd2lzZSB2YXJpYWJsZXMgdGhhdCBoYXZlIG5lZ2F0aXZlIGFuZCB3ZWFrIGNvcnJlbGF0aW9ucywgYW5kIG1vc3Qgb2YgdGhlIHZhcmlhYmxlcyBhcmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggY2lnc1BlckRheS4KCgpgYGB7cn0KCiMgU3RlcCAxOiBGaWx0ZXIgZm9yIG51bWVyaWMgdmFyaWFibGVzLCBleGNsdWRpbmcgc3BlY2lmaWMgY29sdW1ucywgdGhvc2Ugd2l0aCAibG9nIiwgInByZWRpY3RlZCIKbnVtZXJpY192YXJzIDwtIGhlYXJ0ZGF0YSAlPiUKICBzZWxlY3Qod2hlcmUoaXMubnVtZXJpYyksIC1jKGphY2trbmlmZV9yZXNpZHVhbHMsIGN1cnJlbnRTbW9rZXIpKSAlPiUKICBzZWxlY3QoLWNvbnRhaW5zKCJsb2ciKSwgLWNvbnRhaW5zKCJwcmVkaWN0ZWQiKSkKCiMgU3RlcCAyOiBDYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcihudW1lcmljX3ZhcnMsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKIyBHZXQgdGhlIGFic29sdXRlIGNvcnJlbGF0aW9uIHZhbHVlcyBhbmQgY29udmVydCB0byBhIGRhdGEgZnJhbWUKY29yX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMudGFibGUoY29yX21hdHJpeCkpCmNvcl9kZiA8LSBjb3JfZGZbb3JkZXIoLWFicyhjb3JfZGYkRnJlcSkpLCBdICAjIFNvcnQgYnkgYWJzb2x1dGUgY29ycmVsYXRpb24KCiMgU3RlcCAzOiBHZXQgdGhlIHRvcCA2IHVuaXF1ZSBjb3JyZWxhdGVkIHZhcmlhYmxlIHBhaXJzLCBleGNsdWRpbmcgcmV2ZXJzZSBkdXBsaWNhdGVzCnRvcF9jb3JfdmFycyA8LSBjb3JfZGYgJT4lCiAgZmlsdGVyKFZhcjEgIT0gVmFyMikgJT4lCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHQoYXBwbHkoLlssIDE6Ml0sIDEsIHNvcnQpKSkpICU+JSAgIyBSZW1vdmUgcmV2ZXJzZSBkdXBsaWNhdGVzCiAgZGlzdGluY3QoVmFyMSwgVmFyMikgJT4lCiAgaGVhZCg2KQoKIyBTdGVwIDQ6IFN1YnNldCB0aGUgb3JpZ2luYWwgbnVtZXJpYyBkYXRhIGZvciB0aGUgdG9wIGNvcnJlbGF0ZWQgdmFyaWFibGVzCnN1YnNldF92YXJzIDwtIHVuaXF1ZShjKHRvcF9jb3JfdmFycyRWYXIxLCB0b3BfY29yX3ZhcnMkVmFyMikpCnN1YnNldF9kYXRhIDwtIG51bWVyaWNfdmFycyAlPiUKICBzZWxlY3QoYWxsX29mKHN1YnNldF92YXJzKSkKCiMgU3RlcCA1OiBDcmVhdGUgc2NhdHRlcnBsb3RzIGZvciBlYWNoIHVuaXF1ZSBwYWlyCmZvciAoaSBpbiAxOm5yb3codG9wX2Nvcl92YXJzKSkgewogIHZhcjFfbmFtZSA8LSB0b3BfY29yX3ZhcnMkVmFyMVtpXQogIHZhcjJfbmFtZSA8LSB0b3BfY29yX3ZhcnMkVmFyMltpXQogIAogICMgQ3JlYXRlIHRoZSBzY2F0dGVycGxvdAogIHByaW50KAogICAgeHlwbG90KGFzLmZvcm11bGEocGFzdGUodmFyMV9uYW1lLCAifiIsIHZhcjJfbmFtZSkpLCBkYXRhID0gc3Vic2V0X2RhdGEsCiAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJTY2F0dGVycGxvdCBvZiIsIHZhcjFfbmFtZSwgInZzIiwgdmFyMl9uYW1lKSwKICAgICAgICAgICB4bGFiID0gdmFyMl9uYW1lLAogICAgICAgICAgIHlsYWIgPSB2YXIxX25hbWUsCiAgICAgICAgICAgcGFuZWwgPSBmdW5jdGlvbiguLi4pIHsKICAgICAgICAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEpICAjIEFkZCBncmlkCiAgICAgICAgICAgICBwYW5lbC54eXBsb3QoLi4uKSAgIyBTY2F0dGVyIHBvaW50cwogICAgICAgICAgIH0pCiAgKQp9CmBgYApGaWd1cmUgVS4gU2NhdHRlcnBsb3RzIGZvciB0b3AgNiBtb3N0IGNvcnJlbGF0ZWQgcGFpcnMgb2Ygb3JpZ2luYWwgdmFyaWFibGVzLiBEaWFzdG9saWMgJiBzeXN0b2xpYyBCUCBhcmUgaGlnaGx5IGxpbmVhcmx5IGNvcnJlbGF0ZWQsIGFzIGNsaW5pY2FsbHkgZXhwZWN0ZWQuIEFsbCBvdGhlciBwYWlycyAoU3lzdG9saWMgQlAgJiBhZ2UsIEJNSSAmIERpYXN0b2xpYy9TeXN0b2xpYyBCUCwgVG90YWwgQ2hvbGVzdGVyb2wgJiBhZ2UsIApUb3RhbCBDaG9sZXN0ZXJvbCAmIFN5c3RvbGljIEJQKSBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkLiAKCgoKYGBge3J9CiNzY2F0dGVycGxvdCBhZ2UgJiBjaWdzUGVyRGF5CmdncGxvdChoZWFydGRhdGEsIGFlcyh4ID0gYWdlLCB5ID0gY2lnc1BlckRheSkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiQWdlIHZzIENpZ2FyZXR0ZXMgUGVyIERheSB3aXRoIFRyZW5kIExpbmUiLAogICAgICAgeCA9ICJBZ2UiLAogICAgICAgeSA9ICJDaWdhcmV0dGVzIFBlciBEYXkiKSArCiAgdGhlbWVfbWluaW1hbCgpCgoKYGBgCkZpZ3VyZSBWLiBBZ2UgJiBDaWdzUGVyRGF5IGlzIHRoZSBtb3N0IHN0cm9uZ2x5IGFuZCBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzLiBBcyBwYXJ0aWNpcGFudHMgaW5jcmVhc2UgaW4gYWdlLCB0aGUgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgcGVyIGRheSBkZWNyZWFzZXMuIAoKCgoKIyBGRUFUVVJFIEVOR0lORUVSIAoKQWNjb3JkaW5nIHRvIHRoZSBDREMsIHNtb2tpbmcgaW5jcmVhc2VzIHRoZSByaXNrIG9mIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2UuIChodHRwczovL3d3dy5jZGMuZ292L3RvYmFjY28vYWJvdXQvY2lnYXJldHRlcy1hbmQtY2FyZGlvdmFzY3VsYXItZGlzZWFzZS5odG1sKS4gQ2FyZGlvdmFzY3VsYXIgZGlzZWFzZSBpbmNsdWRlcyBoZWFydCBkaXNlYXNlLCBzdHJva2UsIGFuZCBwZXJpcGhlcmFsIGFydGVyeSBkaXNlYXNlLiBDSEQsIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UsIGlzIHRoZSBtb3N0IGNvbW1vbiB0eXBlcyBvZiBoZWFydCBkaXNlYXNlIGluIHRoZSBVUy4gCgpDaWdhcmV0dGVzIHBlciBkYXksIGFnZSwgYW5kIHRvdGFsIGNob2xlc3Rlcm9sIGFyZSBhbGwgdmFyaWFibGVzIGluIHRoZSBGcmFtaW5naGFtIEhlYXJ0IERhdGEgc3Vic2V0IHRoYXQgYXJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIENIRC4gVG8gY2FwdHVyZSB0aGUgY29tYmluZWQgZWZmZWN0cyBvZiB0aGVzZSBmYWN0b3JzLCBhIG5ldyB2YXJpYWJsZSBuYW1lZCAic21va2luZ19yaXNrX2luZGV4IiB3YXMgY3JlYXRlZC4gVGhpcyB2YXJpYWJsZSBlcXVhbGx5IHdlaWdodHMgY2lnc1BlckRheSwgYWdlLCBhbmQgdG90Q2hvbC4gVGhlIHZhbHVlcyBvZiBzbW9raW5nX3Jpc2tfaW5kZXggcmFua3MgaW5kaXZpZHVhbHMgYmFzZWQgb24gdGhlaXIgc21va2luZyBiZWhhdmlvciwgYWdlLCBhbmQgY2hvbGVzdGVyb2wgbGV2ZWxzLiBUaGUgaGlnaGVyIHRoZSBpbmRleCB2YWx1ZSwgdGhlIGhpZ2hlciBhbiBpbmRpdmlkdWFsIGlzIGF0IHJpc2sgZm9yIENIRC4gCgoKYGBge3J9CgoKI2NyZWF0ZSBzbW9raW5nIHJpc2sgaW5kZXggdXNpbmcgd2VpZ2h0aW5nCgpoZWFydGRhdGEkc21va2luZ19yaXNrX2luZGV4IDwtIChoZWFydGRhdGEkY2lnc1BlckRheSAqIDAuMzMzKSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVhcnRkYXRhJGFnZSAqIDAuMzMzKSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVhcnRkYXRhJHRvdENob2wqIDAuMzMzKQojaGVhZChoZWFydGRhdGEpCgoKZ2dwbG90KGhlYXJ0ZGF0YSwgYWVzKHggPSBzbW9raW5nX3Jpc2tfaW5kZXgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgZmlsbCA9ICJibHVlIiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU21va2luZyBSaXNrIEluZGV4IiwgCiAgICAgICB4ID0gIlNtb2tpbmcgUmlzayBJbmRleCIsIAogICAgICAgeSA9ICJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgR2VuZXJhdGUgUVEgcGxvdCBmb3Igc21va2luZ19yaXNrX2luZGV4CnFxbm9ybShoZWFydGRhdGEkc21va2luZ19yaXNrX2luZGV4LCBtYWluID0gIlFRIFBsb3Qgb2YgU21va2luZyBSaXNrIEluZGV4IikKcXFsaW5lKGhlYXJ0ZGF0YSRzbW9raW5nX3Jpc2tfaW5kZXgsIGNvbCA9ICJyZWQiKQoKI3N1bW1hcnkgc3RhdGlzdGljcwpzdW1tYXJ5KGhlYXJ0ZGF0YSRzbW9raW5nX3Jpc2tfaW5kZXgpCiMgU3RhbmRhcmQgZGV2aWF0aW9uCiNzZChoZWFydGRhdGEkc21va2luZ19yaXNrX2luZGV4KQoKCmBgYAoKRmlndXJlIFcuIFRoZSBzbW9raW5nIHJpc2sgaW5kZXggZGlzdHJpYnV0aW9uIGlzIHJvdWdobHkgc3ltbWV0cmljIHdpdGggYSBtZWFuIGluZGV4IG9mIDk4LjM1IGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMTYuMDIuIFRoZSB2YWx1ZXMgb2Ygc21va2luZ19yaXNrX2luZGV4IHZhcmllcyBmcm9tIDU2LjI4IHRvIDI1MS43NS4gQWJvdXQgMjUlIG9mIHBhcnRpY2lwYW50cyBoYXZlIHNtb2tpbmdfcmlza19pbmRleCBhdCBvciBiZWxvdyA4Ny41OCBhbmQgYWJvdXQgMjUlIG9mIHBhcnRpY2lwYW50cyBoYXZlIHNtb2tpbmcgcmlzayBpbmRleCB2YWx1ZXMgYXQgb3IgZ3JlYXRlciB0aGFuIDEwOC41Ni4gVGhlIFFRIHBsb3QgaW5kaWNhdGVzIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBzbW9raW5nX3Jpc2tfaW5kZXggaXMgYXBwcm94aW1hdGVseSBub3JtYWwuIAoKCgpgYGB7cn0KCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIFN0ZXAgMTogQ3JlYXRlIGEgNSUgYm9vdHN0cmFwIHNhbXBsZSAoc2FtcGxpbmcgd2l0aCByZXBsYWNlbWVudCkKbiA8LSBucm93KGhlYXJ0ZGF0YSkKYm9vdHN0cmFwX3NhbXBsZSA8LSBoZWFydGRhdGFbc2FtcGxlKDE6biwgc2l6ZSA9IDAuMSAqIG4sIHJlcGxhY2UgPSBUUlVFKSwgXQoKIyBTdGVwIDI6IFJlc2hhcGUgdGhlIGRhdGEgdG8gbG9uZyBmb3JtYXQgZm9yIGdncGxvdApsb25nX2RhdGEgPC0gYm9vdHN0cmFwX3NhbXBsZSAlPiUKICBzZWxlY3Qoc21va2luZ19yaXNrX2luZGV4LCBzeXNCUCwgZGlhQlAsIEJNSSwgaGVhcnRSYXRlLCBnbHVjb3NlKSAlPiUKICBwaXZvdF9sb25nZXIoLXNtb2tpbmdfcmlza19pbmRleCwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQoKIyBTdGVwIDM6IENyZWF0ZSBzY2F0dGVycGxvdHMgd2l0aCBmYWNldHMKZ2dwbG90KGxvbmdfZGF0YSwgYWVzKHggPSBzbW9raW5nX3Jpc2tfaW5kZXgsIHkgPSB2YWx1ZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnModGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gU21va2luZyBSaXNrIEluZGV4IGFuZCBPdGhlciBWYXJpYWJsZXMgKEJvb3RzdHJhcCA1JSBTYW1wbGUpIiwKICAgICAgIHggPSAiU21va2luZyBSaXNrIEluZGV4IiwKICAgICAgIHkgPSAiVmFsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYApGaWd1cmUgWC4gVGhlIHNjYXR0ZXJwbG90cyB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBmZWF0dXJlIGVuZ2luZWVyLCBzbW9raW5nX3Jpc2tfaW5kZXgsIGFnYWluc3Qgb3JpZ2luYWwgbnVtZXJpYyB2YXJpYWJsZXMgdGhhdCBhcmUgbm90IGZhY3RvcnMuIFRoZSByZWxhdGlvbnNoaXBzIGFyZSB3ZWFrIHdpdGggQk1JLCBoZWFydFJhdGUsIGFuZCBzeXN0b2xpYyBCUCB2YXJpYWJsZXMuIFRoZSByZWxhdGlvbnNoaXAgd2l0aCBnbHVjb3NlIGlzIG5vdCByZW1hcmthYmxlLCBlaXRoZXIuIAoKQSBzbGlnaHQgbGluZWFyIHJlbGF0aW9uc2hpcCBjYW4gYmUgc2VlbiB3aXRoIGRpYXN0b2xpYyBCUC4gCgpgYGB7cn0KCgpuIDwtIG5yb3coaGVhcnRkYXRhKQpib290c3RyYXBfc2FtcGxlMiA8LSBoZWFydGRhdGFbc2FtcGxlKDE6biwgc2l6ZSA9IDAuMiAqIG4sIHJlcGxhY2UgPSBUUlVFKSwgXQoKZ2dwbG90KGRhdGEgPSBib290c3RyYXBfc2FtcGxlMiwgYWVzKHggPSBzbW9raW5nX3Jpc2tfaW5kZXgsIHkgPSBkaWFCUCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDAuNikgKwogIGdndGl0bGUoIlNjYXR0ZXJwbG90IG9mIERpYXN0b2xpYyBCUCBhbmQgU21va2luZyBSaXNrIEluZGV4IikgKwogIHhsYWIoIlNtb2tpbmcgUmlzayBJbmRleCIpICsKICB5bGFiKCJEaWFzdG9saWMgQlAiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCmBgYApGaWd1cmUgWS4gQSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gc21va2luZyByaXNrIGluZGV4IGFuZCBkaWFzdG9saWMgQlAgY2FuIGJlIHNlZSBpbiB0aGUgc2NhdHRlcnBsb3QuIFRoZSByZWxhdGlvbnNoaXAgaXMgbGlrZWx5IGR1ZSB0byB0aGUgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gZGlhc3RvbGljIEJQIGFuZCBzeXN0b2xpYyBCUCwgYW5kIHN5c3RvbGljIEJQIGlzIGEgZmFjdG9yIGluIHRoZSBzbW9raW5nIHJpc2sgaW5kZXggdmFyaWFibGUuIAoKCiMgQVNTT0NJQVRJT04gQU5BTFlTSVMgSQoKVGhpcyBhbmFseXNpcyBleGFtaW5lcyBzbW9raW5nIGFuZCBzZXggZm9yIHBhcnRpY2lwYW50cy4gSG93IGRvZXMgc21va2luZyBpbXBhY3QgbWV0cmljcyBpbiB0aGUgRnJhbWluZ2hhbSBIZWFydCBTdHVkeT8gSXMgc2V4IGEgc2lnbmlmaWNhbnQgZmFjdG9yIGluIHNvbWUgbWV0cmljcz8KCiMjIFNtb2tlciBBbmFseXNpcwoKClRvIGFuYWx5emUgdGhlIGNpZ3NQZXJEYXksIGEgbmV3IHZhcmlhYmxlIG5hbWVkICJzbW9raW5nX2NhdGVnb3J5IiBpcyBjcmVhdGVkLiBQYXJ0aWNpcGFudHMgd2hvIHNtb2tlIDIwIG9yIG1vcmUgY2lnYXJldHRlcyBwZXIgZGF5IGFyZSBsYWJlbGxlZCAiaGVhdnkiLiBQYXJ0aWNpcGFudHMgd2hvIHNtb2tlIGxlc3MgdGhhbiAyMCBjaWdhcmV0dGVzIHBlciBkYXkgYXJlIGxhYmVsbGVkICJtb2RlcmF0ZSIuIEFsbCBub24tc21va2VycyBoYXZlIGJlZW4gZXhjbHVkZWQgZnJvbSB0aGlzIGFuYWx5c2lzLiBUaGlzIHZhcmlhYmxlIGZhY2lsaXRhdGVzIGNvbXBhcmlzb25zIGJldHdlZW4gZ3JvdXBzLgoKCgpgYGB7cn0KCiMgTG9hZCB0aGUgcmVxdWlyZWQgbGlicmFyaWVzCmxpYnJhcnkoZHBseXIpCgojIENyZWF0ZSBoZWFydGRhdGEzIGZyb20gaGVhcnRkYXRhCmhlYXJ0ZGF0YTMgPC0gaGVhcnRkYXRhICU+JQogIG11dGF0ZShzbW9raW5nX2NhdGVnb3J5ID0gaWZlbHNlKGNpZ3NQZXJEYXkgPj0gMjAsICJoZWF2eSIsICJtb2RlcmF0ZSIpKSAlPiUKICBmaWx0ZXIoY3VycmVudFNtb2tlciA9PSAxKQoKYGBgCgoKYGBge3J9CgojIFN0ZXAgMTogQ3JlYXRlIHRoZSBoZWFydGRhdGEzIGRhdGFzZXQKaGVhcnRkYXRhMyA8LSBoZWFydGRhdGFbaGVhcnRkYXRhJGN1cnJlbnRTbW9rZXIgPT0gMSwgXSAgIyBGaWx0ZXIgb25seSBjdXJyZW50IHNtb2tlcnMKaGVhcnRkYXRhMyRzbW9raW5nX2NhdGVnb3J5IDwtIGlmZWxzZShoZWFydGRhdGEzJGNpZ3NQZXJEYXkgPj0gMjAsICJoZWF2eSIsICJtb2RlcmF0ZSIpICAjIENyZWF0ZSBzbW9raW5nX2NhdGVnb3J5CgojIFN0ZXAgMjogTGlzdCBvZiB2YXJpYWJsZXMgdG8gY29tcGFyZSB3aXRoIHNtb2tpbmdfY2F0ZWdvcnkKdmFyaWFibGVzIDwtIGMoInByZXZhbGVudFN0cm9rZSIsICJwcmV2YWxlbnRIeXAiLCAiZGlhYmV0ZXMiLCAibWFsZSIsICJCUE1lZHMiLCAiVGVuWWVhckNIRCIpCgojIENyZWF0ZSBhIGxpc3QgdG8gc3RvcmUgdGhlIHBsb3RzCnBsb3RfbGlzdCA8LSBsaXN0KCkKCiMgU3RlcCAzOiBMb29wIHRocm91Z2ggZWFjaCB2YXJpYWJsZSB0byBjcmVhdGUgbW9zYWljIHBsb3RzCmZvciAodmFyIGluIHZhcmlhYmxlcykgewogIHAgPC0gZ2dwbG90KGRhdGEgPSBoZWFydGRhdGEzKSArCiAgICBnZW9tX21vc2FpYyhhZXMoeCA9IHByb2R1Y3Qoc21va2luZ19jYXRlZ29yeSwgISFzeW0odmFyKSksIGZpbGwgPSBzbW9raW5nX2NhdGVnb3J5KSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJTbW9raW5nIEN0Z3J5IHZzIiwgdmFyKSwKICAgICAgICAgeCA9IHZhciwKICAgICAgICAgeSA9ICJTbW9raW5nIEN0Z3J5IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiaGVhdnkiID0gImJsdWUiLCAibW9kZXJhdGUiID0gInJlZCIpKSArICAjIEN1c3RvbWl6ZSBjb2xvcnMgYXMgbmVlZGVkCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwgICMgU21hbGxlciB0aXRsZSBzaXplCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDEwKSwgICMgQWRqdXN0IHktYXhpcyB0aXRsZSBzaXplCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICMgQWRqdXN0IHgtYXhpcyB0aXRsZSBzaXplCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSBsZWdlbmQKICBwbG90X2xpc3RbW3Zhcl1dIDwtIHAKfQoKIyBTdGVwIDQ6IEFycmFuZ2UgdGhlIHBsb3RzIGluIGEgZ3JpZCB3aXRoIGNvbnRyb2xsZWQgc2l6ZQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsb3RfbGlzdCwgbmNvbCA9IDIsIAogICAgICAgICAgICAgdG9wID0gZ3JpZDo6dGV4dEdyb2IoIkNvbXBhcmlzb24gb2YgU21va2luZyBDYXRlZ29yeSB3aXRoIFZhcmlvdXMgSGVhbHRoIE91dGNvbWVzIiwgZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTIsIGZvbnRmYWNlID0gImJvbGQiKSksICAjIFNtYWxsZXIgdG9wIHRpdGxlCiAgICAgICAgICAgICBwYWRkaW5nID0gdW5pdCgyLCAibGluZXMiKSkKCgpgYGAKCkZpZ3VyZSBaLiBIZWF2eSBzbW9rZXJzIGluIHRoaXMgc3R1ZHkgYXJlIG1vcmUgZnJlcXVlbnRseSBtYWxlLCBoYXZlIGxvd2VyIGluY2lkZW50cyBvZiBzdHJva2UsIGFuZCBoYXZlIHNsaWdodGx5IGhpZ2hlciBpbmNpZGVuY2Ugb2YgMTAgeWVhciByaXNrIG9mIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UuIE5vIGFzc29jaWF0aW9uIHdpdGggc21va2luZyBjYXRlZ29yeSBjYW4gYmUgdmlzdWFsaXplZCBpbiB0aGUgbW9zYWljIHBsb3RzIGJldHdlZW4gcHJldmFsZW50IGh5cGVydGVuc2l2ZSBhbmQgZGlhYmV0ZXMuIAoKClN5c3RvbGljIEJQIGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlcmVmb3JlLCBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBpcyBkb25lIHRvIGNvbmR1Y3QgYW4gQU5PVkEgYW5hbHlzaXMgd2l0aCBzbW9raW5nIHN0YXR1cyAoY3VycmVudFNtb2tlcikuIExldmVuZSdzIFRlc3QgaW5kaWNhdGVzIHRoYXQgdGhlIHZhcmlhbmNlcyBhcmUgdW5lcXVhbC4gVGhlcmVmb3JlLCBXZWxjaCdzIEFOT1ZBIGlzIGNvbmR1Y3RlZC4gQmFzZWQgb24gdGhlIGF2YWlsYWJsZSBkYXRhLCB0aGVyZSBpcyBldmlkZW5jZSBvZiBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gc3lzdG9saWMgQlAgYmV0d2VlbiBzbW9rZXJzIGFuZCBub24tc21va2Vycy4gCgpgYGB7cn0KIyBDcmVhdGUgYSBib3hwbG90IG9mIHN5c0JQIGJ5IGN1cnJlbnRTbW9rZXIKZ2dwbG90KGhlYXJ0ZGF0YSwgYWVzKHggPSBmYWN0b3IoY3VycmVudFNtb2tlciksIHkgPSBzeXNCUCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJza3libHVlIikgKwogIGxhYnModGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoc3lzQlApIGJ5IFNtb2tpbmcgU3RhdHVzIiwKICAgICAgIHggPSAiQ3VycmVudCBTbW9rZXIgKDAgPSBObywgMSA9IFllcykiLAogICAgICAgeSA9ICJTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoc3lzQlApIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBBcHBseSBsb2cgdHJhbnNmb3JtYXRpb24gdG8gc3lzQlAKaGVhcnRkYXRhJGxvZ19zeXNCUCA8LSBsb2coaGVhcnRkYXRhJHN5c0JQKQoKCiMgUGVyZm9ybSBXZWxjaCdzIEFOT1ZBIHdpdGggdHJhbnNmb3JtZWQgc3lzQlAKd2VsY2hfYW5vdmFfcmVzdWx0IDwtIG9uZXdheS50ZXN0KGxvZ19zeXNCUCB+IGN1cnJlbnRTbW9rZXIsIGRhdGEgPSBoZWFydGRhdGEsIHZhci5lcXVhbCA9IEZBTFNFKQoKIyBTdW1tYXJ5IG9mIFdlbGNoJ3MgQU5PVkEgcmVzdWx0CndlbGNoX2Fub3ZhX3Jlc3VsdApgYGAKCkZpZ3VyZSBBQS4gV2VsY2gncyBBTk9WQSByZXN1bHRzIGFyZSBzaWduaWZpY2FudC4gQmFzZWQgb24gdGhlIGF2YWlsYWJsZSBkYXRhLCB0aGVyZSBpcyBldmlkZW5jZSBvZiBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gc3lzdG9saWMgQlAgYmV0d2VlbiBzbW9rZXJzIGFuZCBub24tc21va2Vycy4gSW4gZmFjdCwgY3VycmVudCBzbW9rZXJzIGhhdmUgbG93ZXIgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgdGhhbiBub24tc21va2Vycy4gCgoKCkRpYXN0b2xpYyBCUCBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoZXJlZm9yZSwgYSBsb2cgdHJhbnNmb3JtYXRpb24gaXMgZG9uZSB0byBjb25kdWN0IGFuIEFOT1ZBIGFuYWx5c2lzIHdpdGggc21va2luZyBzdGF0dXMgKGN1cnJlbnRTbW9rZXIpLiBCYXNlZCBvbiBMZXZlbmUncyBUZXN0LCB0aGUgdmFyaWFuY2VzIGFyZSB0aGUgc2FtZS4gSW4gdHVybiwgYXNzdW1wdGlvbnMgYXJlIG1ldC4gQmFzZWQgb24gdGhlIGF2YWlsYWJsZSBkYXRhLCB0aGVyZSBpcyBldmlkZW5jZSBvZiBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gZGlhc3RvbGljIEJQIGJldHdlZW4gc21va2VycyBhbmQgbm9uLXNtb2tlcnMuIAoKYGBge3J9CiMgQ3JlYXRlIGEgYm94cGxvdCBvZiBkaWFCUCBieSBjdXJyZW50U21va2VyCmdncGxvdChoZWFydGRhdGEsIGFlcyh4ID0gZmFjdG9yKGN1cnJlbnRTbW9rZXIpLCB5ID0gZGlhQlApKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiZ3JlZW4iKSArCiAgbGFicyh0aXRsZSA9ICJDb21wYXJpc29uIG9mIERpYXN0b2xpYyBCbG9vZCBQcmVzc3VyZSAoc3lzQlApIGJ5IFNtb2tpbmcgU3RhdHVzIiwKICAgICAgIHggPSAiQ3VycmVudCBTbW9rZXIgKDAgPSBObywgMSA9IFllcykiLAogICAgICAgeSA9ICJEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUgKGRpYUJQKSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgQXBwbHkgbG9nIHRyYW5zZm9ybWF0aW9uIHRvIHN5c0JQCmhlYXJ0ZGF0YSRsb2dfZGlhQlAgPC0gbG9nKGhlYXJ0ZGF0YSRkaWFCUCkKCiMgUGVyZm9ybSBMZXZlbmUncyBUZXN0IGZvciBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZXMKbGV2ZW5lVGVzdChsb2dfZGlhQlAgfiBjdXJyZW50U21va2VyLCBkYXRhID0gaGVhcnRkYXRhKQoKIyBQZXJmb3JtIEFOT1ZBIHdpdGggdHJhbnNmb3JtZWQgc3lzQlAKYW5vdmFfcmVzdWx0MiA8LSBhb3YobG9nX2RpYUJQIH4gY3VycmVudFNtb2tlciwgZGF0YSA9IGhlYXJ0ZGF0YSkKCiMgU3VtbWFyeSBvZiBBTk9WQSByZXN1bHQKc3VtbWFyeShhbm92YV9yZXN1bHQyKQpgYGAKRmlndXJlIEJCLiBUaGUgQU5PVkEgYW5hbHlzaXMgd2FzIHNpZ25pZmljYW50LiBCYXNlZCBvbiB0aGUgYXZhaWxhYmxlIGRhdGEsIHRoZXJlIGlzIGV2aWRlbmNlIG9mIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBkaWFzdG9saWMgQlAgYmV0d2VlbiBzbW9rZXJzIGFuZCBub24tc21va2Vycy4gU2ltaWxhciB0byBmaW5kaW5ncyBmb3Igc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIHNtb2tlcnMgaGF2ZSBsb3dlciBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgY29tcGFyZWQgdG8gbm9uLXNtb2tlcnMuIAoKCmBgYHtyfQoKIyBDcmVhdGUgdGhlIG5ldyB2YXJpYWJsZXMgYWdlX2dyb3VwIGFuZCBnbHVjb3NlX2xldmVsCmhlYXJ0ZGF0YSA8LSBoZWFydGRhdGEgJT4lCiAgbXV0YXRlKAogICAgIyBDcmVhdGUgYWdlX2dyb3VwIGJhc2VkIG9uIGFnZQogICAgYWdlX2dyb3VwID0gY2FzZV93aGVuKAogICAgICBhZ2UgPD0gNTkgfiAibWlkZGxlLWFnZWQiLAogICAgICBhZ2UgPj0gNjAgfiAib2xkZXIiCiAgICApCiAgKQoKIyBCb3hwbG90IGZvciBhZ2VfZ3JvdXAgYW5kIGNpZ3NQZXJEYXkKZ2dwbG90KGhlYXJ0ZGF0YSwgYWVzKHggPSBhZ2VfZ3JvdXAsIHkgPSBjaWdzUGVyRGF5KSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gImJsdWUiKSArCiAgbGFicyh0aXRsZSA9ICJDaWdhcmV0dGVzIHBlciBEYXkgYnkgQWdlIEdyb3VwIiwKICAgICAgIHggPSAiQWdlIEdyb3VwIiwgCiAgICAgICB5ID0gIkNpZ2FyZXR0ZXMgcGVyIERheSIpICsKICB0aGVtZV9taW5pbWFsKCkKCgoKYGBgCkZpZ3VyZSBDQy4gTWlkZGxlLWFnZWQgYWR1bHRzIGFyZSA1OSB5ZWFycyBvbGQgb3IgeW91bmcuIE9sZGVyIGFkdWx0cyBhcmUgNjAgb3Igb2xkZXIuIE1pZGRsZSBhZ2VkIGFkdWx0cyB3aG8gc21va2UgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgdGVuZCB0byBzbW9rZSBtb3JlIGNpZ2FyZXR0ZXMgcGVyIGRheSB0aGFuIG9sZGVyIGFkdWx0cyB3aG8gc21va2UgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuICAKCgoKCgpgYGB7cn0KCmdncGxvdChkYXRhID0gaGVhcnRkYXRhLCBhZXMoeCA9IHN5c0JQLCBmaWxsID0gVGVuWWVhckNIRCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpICsKICBnZ3RpdGxlKCJUZW4gWWVhciBDSEQgdnMgU3lzdG9saWMgQlAiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKwogIHhsYWIoIlN5c3RvbGljIEJQIikgKyAKICB5bGFiKCJUZW4gWWVhciBDSEQiKQoKYGBgCkZpZ3VyZSBERC4gUGFydGljaXBhbnRzIHdobyB3ZXJlIGF0IHJpc2sgZm9yIDEwIHllYXIgQ0hEIGhhdmUgaGlnaGVyIHN5c3RvbGljIEJQIHRoYW4gZm9yIHBhcnRpY2lwYW50cyB3aG8gd2VyZSBub3QgYXQgcmlzayBmb3IgMTAgeWVhciBDSEQuIAoKYGBge3J9CgpoZWFydGRhdGEkcHJldmFsZW50SHlwIDwtIGFzLmNoYXJhY3RlcihoZWFydGRhdGEkcHJldmFsZW50SHlwKQpoZWFydGRhdGEkY3VycmVudFNtb2tlciA8LSBhcy5jaGFyYWN0ZXIoaGVhcnRkYXRhJGN1cnJlbnRTbW9rZXIpCmhlYXJ0ZGF0YSRCUE1lZHMgPC0gYXMuY2hhcmFjdGVyKGhlYXJ0ZGF0YSRCUE1lZHMpCmhlYXJ0ZGF0YSRtYWxlIDwtIGFzLmNoYXJhY3RlcihoZWFydGRhdGEkbWFsZSkKaGVhcnRkYXRhJGVkdWNhdGlvbiA8LSBhcy5jaGFyYWN0ZXIoaGVhcnRkYXRhJGVkdWNhdGlvbikKaGVhcnRkYXRhJHByZXZhbGVudFN0cm9rZSA8LSBhcy5jaGFyYWN0ZXIoaGVhcnRkYXRhJHByZXZhbGVudFN0cm9rZSkKaGVhcnRkYXRhJGRpYWJldGVzIDwtIGFzLmNoYXJhY3RlcihoZWFydGRhdGEkZGlhYmV0ZXMpCgojc3RyKGhlYXJ0ZGF0YSkKCmdncGxvdChkYXRhID0gaGVhcnRkYXRhLCBhZXMoeCA9IGRpYUJQLCBmaWxsID0gcHJldmFsZW50SHlwKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKwogIGdndGl0bGUoIlByZXZhbGVudCBIeXBlcnRlbnNpdmUgdnMgRGlhc3RvbGljIEJQIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb249InRvcCIpICsKICB4bGFiKCJEaWFzdG9saWMgQlAiKSArIAogIHlsYWIoIlByZXZhbGVudCBIeXBlcnRlbnNpdmUiKQoKYGBgCkZpZ3VyZSBFRS4gRm9yIHByZXZhbGVudCBoeXBlcnRlbnNpdmUgcGFydGljaXBhbnRzLCB0aGUgdHlwaWNhbCBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgaXMgaGlnaGVyIGNvbXBhcmVkIHRvIHRob3NlIHdobyBhcmUgbm90IHByZXZhbGVudCBoeXBlcnRlbnNpdmUsIGFzIHNlZW4gaW4gdGhlIGdyYXBoLiBJbiB0dXJuLCB0aGVyZSBpcyBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIHByZXZhbGVudCBoeXBlcnRlbnNpdmUgYW5kIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZS4gIAoKCgojIyBTZXggQW5hbHlzaXMKClNleCBhbmFseXNpcyBtYXkgYmUgaW5mb3JtYXRpdmUgaW4gdGhlIGNvbnRleHQgb2YgaGVhbHRoIGR1ZSB0byBpbmhlcmVudCBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzLiBUaGUgZGlmZmVyZW5jZXMgaW4gdGVuIHllYXIgcmlzayBmb3IgQ0hEIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50LiBJbiBmYWN0LCBtYWxlIGluIHRoaXMgc3R1ZHkgaGF2ZSBhIGhpZ2hlciB0ZW4geWVhciByaXNrIGZvciBDSEQgdGhhbiBmZW1hbGUuCgoKYGBge3J9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoZ2dtb3NhaWMpCgojIENyZWF0ZSB0aGUgbW9zYWljIHBsb3QKbW9zYWljcGxvdCh+IFRlblllYXJDSEQgKyBzZXgsIAogICAgICAgICAgIGRhdGEgPSBoZWFydHZpeiwgCiAgICAgICAgICAgY29sb3IgPSBjKCJibHVlIiwgInllbGxvdyIpLCAKICAgICAgICAgICBtYWluID0gIk1vc2FpYyBQbG90IG9mIFRlblllYXJDSEQgYW5kIFNleCIsCiAgICAgICAgICAgeGxhYiA9ICJUZW5ZZWFyQ0hEIiwgCiAgICAgICAgICAgeWxhYiA9ICJTZXgiKQpgYGAKRmlndXJlIEZGLiBNYWxlIHBhcnRpY2lwYW50cyBpbiB0aGUgc3R1ZHkgaGF2ZSBhIGhpZ2hlciBwcmVkaWN0ZWQgdGVuIHllYXIgQ0hEIHJpc2sgY29tcGFyZWQgdG8gZmVtYWxlIHBhcnRpY2lwYW50cyBpbiB0aGUgc3R1ZHkuICAKCgpUbyBmdXJ0aGVyIGludmVzdGlnYXRlIHRoZSBhc3NvY2lhdGlvbiB2aXN1YWxpemVkIGluIHRoZSBtb3NhaWMgcGxvdCwgYSBDaGkgU3F1YXJlIFRlc3Qgb2YgR2VuZXJhbCBBc3NvY2lhdGlvbiBpcyBjb25kdWN0ZWQgKFRhYmxlIDIpLiBBbGwgZXhwZWN0ZWQgY2VsbCBjb3VudHMgYXJlIGVxdWFsIHRvIG9yIGdyZWF0ZXIgdGhhbiA1LiBUaGVyZWZvcmUsIHRoZSB0ZXN0IGlzIGp1c3RpZmllZC4KCkhvOiBUaGVyZSBpcyBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIHNleCBhbmQgVGVuWWVhckNIRC4KSGE6IFRoZXJlIGlzIGFuIGFzc29jaWF0aW9uIGJldHdlZW4gc2V4IGFuZCBUZW5ZZWFyQ0hECgpUaGVyZSBpcyBldmlkZW5jZSB0byBzdWdnZXN0IHRoYXQgYW4gYXNzb2NpYXRpb24gZXhpc3RzIGJldHdlZW4gc2V4IGFuZCBUZW5ZZWFyQ0hELiAKCmBgYHtyfQojIFN0ZXAgMTogQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKY29udGluZ2VuY3lfdGFibGUgPC0gdGFibGUoaGVhcnRkYXRhJG1hbGUsIGhlYXJ0ZGF0YSRUZW5ZZWFyQ0hEKQoKIyBTdGVwIDI6IERpc3BsYXkgdGhlIGNvbnRpbmdlbmN5IHRhYmxlCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQoKIyBTdGVwIDM6IFBlcmZvcm0gdGhlIENoaS1zcXVhcmUgdGVzdApjaGlfc3F1YXJlX3Jlc3VsdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQoKIyBTdGVwIDQ6IERpc3BsYXkgdGhlIHJlc3VsdHMKcHJpbnQoY2hpX3NxdWFyZV9yZXN1bHQpCgpgYGAKVGFibGUgMi4gQSBnZW5lcmFsIHRlc3Qgb2YgYXNzb2NpYXRpb24gYmV0d2VlbiBzZXggYW5kIFRlblllYXJDSEQgcmVzdWx0cyBpbiBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIG1hbGUgYW5kIGZlbWFsZSBwYXJ0aWNpcGFudHMuIEluIGZhY3QsIG1hbGUgcGFydGljaXBhbnRzIGhhdmUgYSBoaWdoZXIgcmlzayBvZiBkZXZlbG9waW5nIENIRCBpbiB0aGUgbmV4dCAxMCB5ZWFycy4gCgoKCiMgQVNTT0NJQVRJT04gQU5BTFlTSVMgSUkgCgojIyBEaWFiZXRlcwoKClRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGRpYWJldGVzIGFuZCBnbHVjb3NlIGlzIDAuNjA1NzA4NjU1LCB3aGljaCBpbmRpY2F0ZXMgYSBzdHJvbmcgcmVsYXRpb25zaGlwLlRoZSBhc3NvY2lhdGlvbiBvZiBzb21lIHZhcmlhYmxlcywgc3VjaCBhcyBhZ2UsIGdsdWNvc2UgbGV2ZWxzLCBhbmQgQk1JIHdpdGggZGlhYmV0ZXMgaXMgYWxzbyBleGFtaW5lZC4gVGhyZWUgbmV3IHZhcmlhYmxlcyBhcmUgY3JlYXRlZCB0byBmYWNpbGl0YXRlIHRoaXMgYW5hbHlzaXMuIAoKQWNjb3JkaW5nIHRvIE5JSCAoaHR0cHM6Ly9wbWMubmNiaS5ubG0ubmloLmdvdi9hcnRpY2xlcy9QTUMxMDQyNTkyMS8pLCAieW91bmciIG1lYW5zIGxlc3MgdGhhbiAzNSB5ZWFycyBvbGQsICJtaWRkbGUtYWdlZCIgbWVhbnMgMzYtNTksIGFuZCAib2xkZXIiIG1lYW5zIDYwKyB5cnMgb2xkLiBTaW5jZSB0aGUgbWluaW11bSBhZ2Ugb2YgcGFydGljaXBhbnRzIGluIHRoaXMgZGF0YSBzdWJzZXQgaXMgMzIsIHRoZSAieW91bmciIGNhdGVnb3J5IGlzIGV4Y2x1ZGVkIGluIHRoZSBhbmFseXNpcy4gQSB2YXJpYWJsZSBjYWxsZWQgYWdlX2dyb3VwIGlzIGNyZWF0ZWQgaW4gdGhlIGRhdGEgc2V0IGFjY29yZGluZyB0byB0aGVzZSBndWlkZWxpbmVzLiAKCk1hbnkgdGVzdHMgYXJlIHVzZWQgdG8gZGlhZ25vc2UgZGlhYmV0ZXMgaW5jbHVkaW5nIGdsdWNvc2UgbGV2ZWxzLiBBY2NvcmRpbmcgdG8gdGhlIENEQyAoaHR0cHM6Ly93d3cuY2RjLmdvdi9kaWFiZXRlcy9kaWFiZXRlcy10ZXN0aW5nL2luZGV4Lmh0bWwpLCBnbHVjb3NlIGxldmVscyBncmVhdGVyIHRoYW4gMTI2IG1nL2RMIGluZGljYXRlcyBkaWFiZXRlcywgbGV2ZWxzIGJldHdlZW4gMTAwIGFuZCAxMjUgbWcvZEwgaW5kaWNhdGVzIHByZWRpYWJldGVzLCBhbmQgbm9ybWFsIGxldmVscyBpcyBtZWFzdXJlZCBhdCA5OSBtZy9kTCBvciBsZXNzLiBBIG5ldyB2YXJpYWJsZSAiZ2x1Y29zZV9sZXZlbCIgaXMgY3JlYXRlZCBiYXNlZCB0aGVzZSBDREMgZ3VpZGVsaW5lcy4gCgpCTUkgaXMgYSBzY3JlZW5pbmcgbWVhc3VyZSB0aGF0IGlzIHVzZWQgd2l0aCBvdGhlciBtZWFzdXJlcyB0byBhc3Nlc3MgaGVhbHRoLiBBIHZhcmlhYmxlICJCTUlfY2F0ZWdvcnkiIGhhcyBiZWVuIGNyZWF0ZWQgdXNpbmcgdGhlIEJNSSBkYXRhLiBBY2NvcmRpbmcgdG8gdGhlIENEQyAoaHR0cHM6Ly93d3cuY2RjLmdvdi9ibWkvYWR1bHQtY2FsY3VsYXRvci9ibWktY2F0ZWdvcmllcy5odG1sKSwgYWR1bHRzIDIwIHllYXJzIG9yIG9sZGVyIHdobyBoYXZlIEJNSSBsZXNzIHRoYW4gMTguNSBhcmUgdW5kZXJ3ZWlnaHQsIDE4LjUgdG8gbGVzcyB0aGFuIDI1IGFyZSBoZWFsdGh5IHdlaWdodCwgMjUgdG8gbGVzcyB0aGFuIDMwIGFyZSBvdmVyd2VpZ2h0LCBhbmQgMzAgb3IgZ3JlYXRlciBhcmUgb2Jlc2UuIAoKYGBge3J9CgojIENyZWF0ZSB0aGUgbmV3IHZhcmlhYmxlcyBhZ2VfZ3JvdXAgYW5kIGdsdWNvc2VfbGV2ZWwKaGVhcnRkYXRhIDwtIGhlYXJ0ZGF0YSAlPiUKICBtdXRhdGUoCiAgICAjIENyZWF0ZSBhZ2VfZ3JvdXAgYmFzZWQgb24gYWdlCiAgICBhZ2VfZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIGFnZSA8PSA1OSB+ICJtaWRkbGUtYWdlZCIsCiAgICAgIGFnZSA+PSA2MCB+ICJvbGRlciIKICAgICksCiAgICAKICAgICMgQ3JlYXRlIGdsdWNvc2VfbGV2ZWwgYmFzZWQgb24gZ2x1Y29zZQogICAgZ2x1Y29zZV9sZXZlbCA9IGNhc2Vfd2hlbigKICAgICAgZ2x1Y29zZSA8PSA5OSB+ICJub3JtYWwiLAogICAgICBnbHVjb3NlID49IDEwMCAmIGdsdWNvc2UgPD0gMTI1IH4gInByZWRpYWJldGVzIiwKICAgICAgZ2x1Y29zZSA+PSAxMjYgfiAiZGlhYmV0ZXMiCiAgICApCiAgKQoKIyBDcmVhdGUgdGhlIEJNSV9jYXRlZ29yeSB2YXJpYWJsZQpoZWFydGRhdGEgPC0gaGVhcnRkYXRhICU+JQogIG11dGF0ZSgKICAgIEJNSV9jYXRlZ29yeSA9IGNhc2Vfd2hlbigKICAgICAgQk1JIDwgMTguNSB+ICJ1bmRlcndlaWdodCIsCiAgICAgIEJNSSA+PSAxOC41ICYgQk1JIDwgMjUgfiAiaGVhbHRoeV93ZWlnaHQiLAogICAgICBCTUkgPj0gMjUgJiBCTUkgPCAzMCB+ICJvdmVyd2VpZ2h0IiwKICAgICAgQk1JID49IDMwIH4gIm9iZXNlIgogICAgKQogICkKCgojIE1vc2FpYyBwbG90IGNvbXBhcmluZyBhZ2VfZ3JvdXAgdG8gZGlhYmV0ZXMKbW9zYWljcGxvdCh0YWJsZShoZWFydGRhdGEkYWdlX2dyb3VwLCBoZWFydGRhdGEkZGlhYmV0ZXMpLCAKICAgICAgICAgICBtYWluID0gIk1vc2FpYyBQbG90OiBBZ2UgR3JvdXAgdnMuIERpYWJldGVzIiwgCiAgICAgICAgICAgeGxhYiA9ICJBZ2UgR3JvdXAiLCAKICAgICAgICAgICB5bGFiID0gIkRpYWJldGVzIFN0YXR1cyIsIAogICAgICAgICAgIGNvbCA9IGMoImxpZ2h0Ymx1ZSIsICJsaWdodGNvcmFsIikpCgojIE1vc2FpYyBwbG90IGNvbXBhcmluZyBnbHVjb3NlX2xldmVsIHRvIGRpYWJldGVzCm1vc2FpY3Bsb3QodGFibGUoaGVhcnRkYXRhJGdsdWNvc2VfbGV2ZWwsIGhlYXJ0ZGF0YSRkaWFiZXRlcyksIAogICAgICAgICAgIG1haW4gPSAiTW9zYWljIFBsb3Q6IEdsdWNvc2UgTGV2ZWwgdnMuIERpYWJldGVzIiwgCiAgICAgICAgICAgeGxhYiA9ICJHbHVjb3NlIExldmVsIiwgCiAgICAgICAgICAgeWxhYiA9ICJEaWFiZXRlcyBTdGF0dXMiLCAKICAgICAgICAgICBjb2wgPSBjKCJsaWdodGdyZWVuIiwgImxpZ2h0cGluayIpKQoKIyBNb3NhaWMgcGxvdCBjb21wYXJpbmcgYWdlX2dyb3VwIHRvIGdsdWNvc2VfbGV2ZWwKbW9zYWljcGxvdCh0YWJsZShoZWFydGRhdGEkYWdlX2dyb3VwLCBoZWFydGRhdGEkZ2x1Y29zZV9sZXZlbCksIAogICAgICAgICAgIG1haW4gPSAiTW9zYWljIFBsb3Q6IEFnZSBHcm91cCB2cy4gR2x1Y29zZSBMZXZlbCIsIAogICAgICAgICAgIHhsYWIgPSAiQWdlIEdyb3VwIiwgCiAgICAgICAgICAgeWxhYiA9ICJHbHVjb3NlIExldmVsIiwgCiAgICAgICAgICAgY29sID0gYygibGlnaHR5ZWxsb3ciLCAibGlnaHRibHVlIikpCgojIENyZWF0ZSBhIG1vc2FpYyBwbG90IGZvciBCTUlfY2F0ZWdvcnkgYW5kIGdsdWNvc2VfbGV2ZWwKbW9zYWljcGxvdCh0YWJsZShoZWFydGRhdGEkQk1JX2NhdGVnb3J5LCBoZWFydGRhdGEkZ2x1Y29zZV9sZXZlbCksCiAgICAgICAgICAgbWFpbiA9ICJNb3NhaWMgUGxvdDogQk1JIENhdGVnb3J5IHZzLiBHbHVjb3NlIExldmVsIiwKICAgICAgICAgICB4bGFiID0gIkJNSSBDYXRlZ29yeSIsCiAgICAgICAgICAgeWxhYiA9ICJHbHVjb3NlIExldmVsIiwKICAgICAgICAgICBjb2wgPSBjKCJsaWdodGJsdWUiLCAibGlnaHRncmVlbiIsICJsaWdodHBpbmsiKSwKICAgICAgICAgICBib3JkZXIgPSAiZ3JheSIpCgojIENyZWF0ZSBhIG1vc2FpYyBwbG90IGZvciBCTUlfY2F0ZWdvcnkgYW5kIGRpYWJldGVzCm1vc2FpY3Bsb3QodGFibGUoaGVhcnRkYXRhJEJNSV9jYXRlZ29yeSwgaGVhcnRkYXRhJGRpYWJldGVzKSwKICAgICAgICAgICBtYWluID0gIk1vc2FpYyBQbG90OiBCTUkgQ2F0ZWdvcnkgdnMuIGRpYWJldGVzIiwKICAgICAgICAgICB4bGFiID0gIkJNSSBDYXRlZ29yeSIsCiAgICAgICAgICAgeWxhYiA9ICJEaWFiZXRlcyBTdGF0dXMiLAogICAgICAgICAgIGNvbCA9IGMoImxpZ2h0Ymx1ZSIsICJsaWdodGdyZWVuIiwgImxpZ2h0cGluayIpLAogICAgICAgICAgIGJvcmRlciA9ICJncmF5IikKCgoKYGBgCkZpZ3VyZSBHRy4gVGhlIEFnZSBHcm91cCB2cy4gRGlhYmV0ZXMgbW9zYWljIHBsb3QgZGlzcGxheXMgYW4gYXNzb2NpYXRpb24gYmV0d2VlbiBhZ2UgZ3JvdXAgYW5kIGRpYWJldGVzLiBPbGRlciBwZW9wbGUgdGVuZCB0byBoYXZlIGEgaGlnaGVyIGZyZXF1ZW5jeSBvZiBkaWFiZXRlcyB0aGFuIG1pZGRsZS1hZ2VkIGFkdWx0cy4gVGhlIGdsdWNvc2UgbGV2ZWwgdGhhdCBpbmRpY2F0ZXMgZGlhYmV0ZXMgaGFzIHRoZSBoaWdoZXN0IGluY2lkZW5jZSBvZiBkaWFiZXRlcyBpbiB0aGUgR2x1Y29zZSBMZXZlbCB2cy4gRGlhYmV0ZXMgbW9zYWljIHBsb3QuIFRoZSBwcmVkaWFiZXRlcyBnbHVjb3NlIGdyb3VwIGhhcyBhIGhpZ2hlciBmcmVxdWVuY3kgb2YgZGlhYmV0ZXMgdGhhbiB0aGUgbm9ybWFsIGdsdWNvc2UgbGV2ZWwgZ3JvdXAuIEFuIGFzc29jaWF0aW9uIGNhbiBiZSBzZWVuIGJldHdlZW4gYWdlIGdyb3VwIGFuZCBnbHVjb3NlIGxldmVsIGluIHRoZSBBZ2UgR3JvdXAgdnMuIEdsdWNvc2UgTGV2ZWwgbW9zYWljIHBsb3QuIE9sZGVyIGFkdWx0cyB0ZW5kIHRvIGhhdmUgaGlnaGVyIGdsdWNvc2UgbGV2ZWxzIHdoaWNoIGluZGljYXRlIG1vcmUgcHJlZGlhYmV0ZXMgYW5kIGRpYWJldGVzIGNvbXBhcmVkIHRvIHRoZSBvdGhlciB0d28geW91bmdlciBhZ2UgZ3JvdXBzLiBBIHBvc3NpYmxlIGFzc29jaWF0aW9uIGNhbiBiZSBzZWVuIGJldHdlZW4gZ2x1Y29zZSBsZXZlbCBhbmQgQk1JIGNhdGVnb3J5LCBlc3BlY2lhbGx5IGJldHdlZW4gdW5kZXJ3ZWlnaHQgYW5kIGhlYWx0aHkgd2VpZ2h0IHZzIG92ZXJ3ZWlnaHQgYW5kIG9iZXNlIGluIHRoZSBCTUkgQ2F0ZWdvcnkgdnMgR2x1Y29zZSBMZXZlbCBtb3NhaWMgcGxvdC4gSW4gdGhlIEJNSSBDYXRlZ29yeSB2cyBEaWFiZXRlcyBtb3NhaWMgcGxvdCwgYSByZWxhdGlvbnNoaXAgY2FuIGJlIHNlZW4gYmV0d2VlbiBoZWFsdGh5IHdlaWdodCBhbmQgb2Jlc2UuIAoKCkluIHR1cm4sIHR3byB3YXkgdGFibGVzIGFyZSBjcmVhdGVkIGFuZCBhcHByb3ByaWF0ZSBhc3NvY2lhdGlvbiB0ZXN0cyBjb25kdWN0ZWQuCgoKIyMgR2x1Y29zZSB2cyBEaWFiZXRlcywgQ29udHJvbGxpbmcgZm9yIEFnZQoKQW5hbHlzaXMgb2YgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gZ2x1Y29zZSBsZXZlbCBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBhZ2UsIHdhcyBjb25kdWN0ZWQgd2l0aCB0aHJlZSBkZWdyZWVzIG9mIGZyZWVkb20gKFRhYmxlIDMpLiBUaGUgY29udGluZ2VuY3kgdGFibGVzIGFyZSAzWDIgb3JkZXJlZC1ub21pbmFsIGFycmF5cy4gQWxsIGNlbGwgY291bnRzIGFyZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gNSBhbmQgYWxsIHJvdyB0b3RhbHMgYXJlIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAyMCB3aGVuIGNvbWJpbmVkIG92ZXIgYWxsIHRhYmxlcy4gVGhlcmVmb3JlLCB0aGUgdGVzdCBpcyBqdXN0aWZpZWQuIAoKSG86IFRoZXJlIGlzIG5vIGFzc29jaWF0aW9uIGJldHdlZW4gZ2x1Y29zZSBsZXZlbCBhbmQgZGlhYmV0ZXMgc3RhdHVzIHdoZW4gY29udHJvbGxpbmcgZm9yIGFnZS4KSGE6IFRoZXJlIGlzIGEgY29ycmVsYXRpb24gYmV0d2VlbiBnbHVjb3NlIGxldmVsIGFuZCBkaWFiZXRlcyBzdGF0dXMgd2hlbiBjb250cm9sbGluZyBmb3IgYWdlLiAKClNpbmNlIHRoZSBwLXZhbHVlIGlzIGFwcHJveGltYXRlbHkgemVybywgd2hpY2ggaXMgbGVzcyB0aGFuIDAuMDUsIHdlIHJlamVjdCB0aGUgbnVsbC4gVGhlcmUgaXMgc3Ryb25nIHNpZ25pZmljYW50IGV2aWRlbmNlIG9mIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBnbHVjb3NlIGxldmVsIGFuZCBkaWFiZXRlcyB3aGVuIGNvbnRyb2xsaW5nIGZvciBhZ2UgZ3JvdXAuIEFzIGdsdWNvc2UgbGV2ZWwgaW5jcmVhc2VzLCB0aGUgZGlhYmV0ZXMgc3RhdHVzIGluY3JlYXNlcy4KCmBgYHtyfQoKIyBFbnN1cmUgdGhlIGZhY3RvcnMgYXJlIG9yZGVyZWQKaGVhcnRkYXRhIDwtIGhlYXJ0ZGF0YSAlPiUKICBtdXRhdGUoCiAgICBhZ2VfZ3JvdXAgPSBmYWN0b3IoYWdlX2dyb3VwLCBsZXZlbHMgPSBjKCJtaWRkbGUtYWdlZCIsICJvbGRlciIpKSwKICAgIGdsdWNvc2VfbGV2ZWwgPSBmYWN0b3IoZ2x1Y29zZV9sZXZlbCwgbGV2ZWxzID0gYygibm9ybWFsIiwgInByZWRpYWJldGVzIiwgImRpYWJldGVzIikpLAogICAgQk1JX2NhdGVnb3J5ID0gZmFjdG9yKEJNSV9jYXRlZ29yeSwgbGV2ZWxzID0gYygidW5kZXJ3ZWlnaHQiLCAiaGVhbHRoeV93ZWlnaHQiLCAib3ZlcndlaWdodCIsICJvYmVzZSIpKQogICkKCiMgQ29udmVydCBnbHVjb3NlX2xldmVsIHRvIGFuIG9yZGVyZWQgZmFjdG9yCmhlYXJ0ZGF0YSRnbHVjb3NlX2xldmVsIDwtIGZhY3RvcihoZWFydGRhdGEkZ2x1Y29zZV9sZXZlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygibm9ybWFsIiwgInByZWRpYWJldGVzIiwgImRpYWJldGVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpCgojIENyZWF0ZSB0aGUgY29udGluZ2VuY3kgdGFibGUgZm9yIGdsdWNvc2UgbGV2ZWwgYW5kIGRpYWJldGVzLCBjb250cm9sbGluZyBmb3IgYWdlIGdyb3VwCmNvbnRpbmdlbmN5X3RhYmxlMSA8LSB0YWJsZShoZWFydGRhdGEkZ2x1Y29zZV9sZXZlbCwgaGVhcnRkYXRhJGRpYWJldGVzLCBoZWFydGRhdGEkYWdlX2dyb3VwKQoKIyBEaXNwbGF5IHRoZSBjb250aW5nZW5jeSB0YWJsZQpwcmludChjb250aW5nZW5jeV90YWJsZTEpCgoKIyBDb25kdWN0IHRoZSBDb2NocmFuLU1hbnRlbC1IYWVuc3plbCB0ZXN0CmNtaF90ZXN0X3Jlc3VsdCA8LSBtYW50ZWxoYWVuLnRlc3QoY29udGluZ2VuY3lfdGFibGUxKQoKIyBQcmludCB0aGUgdGVzdCByZXN1bHRzCmNhdCgiQ29jaHJhbi1NYW50ZWwtSGFlbnN6ZWwgVGVzdCBSZXN1bHRzOlxuIikKcHJpbnQoY21oX3Rlc3RfcmVzdWx0KQoKYGBgClRhYmxlIDMuIFRoZSBjb3JyZWxhdGlvbiB0ZXN0IGJldHdlZW4gZ2x1Y29zZSBsZXZlbCBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBhZ2UsIHdhcyBzaWduaWZpY2FudC4KCgojIyBCTUkgQ2F0ZWdvcnkgdnMgRGlhYmV0ZXMsIENvbnRyb2wgZm9yIEdsdWNvc2UgTGV2ZWwKCkFuYWx5c2lzIG9mIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBnbHVjb3NlIGxldmVsLCB3YXMgY29uZHVjdGVkIChUYWJsZSA0KS4gVGhlIGNvbnRpbmdlbmN5IHRhYmxlcyBhcmUgNFgyIG9yZGVyZWQtbm9taW5hbCBhcnJheXMuIEFib3V0IDk0JSBvZiBjZWxsIGNvdW50cyBhcmUgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDUgYW5kIGFsbCByb3cgdG90YWxzIGFyZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMjAgd2hlbiBjb21iaW5lZCBvdmVyIGFsbCB0YWJsZXMuIFRoZXJlZm9yZSwgdGhlIHRlc3QgaXMganVzdGlmaWVkLiAKCkhvOiBUaGVyZSBpcyBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMgc3RhdHVzIHdoZW4gY29udHJvbGxpbmcgZm9yIGdsdWNvc2UgbGV2ZWwuCkhhOiBUaGVyZSBpcyBhIGNvcnJlbGF0aW9uIGJldHdlZW4gQk1JIGNhdGVnb3J5IGFuZCBkaWFiZXRlcyBzdGF0dXMgd2hlbiBjb250cm9sbGluZyBmb3IgZ2x1Y29zZSBsZXZlbC4gCgpTaW5jZSB0aGUgcC12YWx1ZSBpcyAwLjAxMjk3LCB3aGljaCBpcyBsZXNzIHRoYW4gMC4wNSwgd2UgcmVqZWN0IHRoZSBudWxsLiBUaGVyZSBpcyBldmlkZW5jZSBvZiBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gQk1JIGNhdGVnb3J5IGFuZCBkaWFiZXRlcyB3aGVuIGNvbnRyb2xsaW5nIGZvciBnbHVjb3NlIGxldmVsLiBBcyBCTUkgY2F0ZWdvcnkgaW5jcmVhc2VzLCB0aGUgZGlhYmV0ZXMgc3RhdHVzIGluY3JlYXNlcy4gCgoKYGBge3J9CgojIENvbnZlcnQgQk1JX2NhdGVnb3J5IHRvIGFuIG9yZGVyZWQgZmFjdG9yCmhlYXJ0ZGF0YSRCTUlfY2F0ZWdvcnkgPC0gZmFjdG9yKGhlYXJ0ZGF0YSRCTUlfY2F0ZWdvcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJ1bmRlcndlaWdodCIsICJoZWFsdGh5X3dlaWdodCIsICJvdmVyd2VpZ2h0IiwgIm9iZXNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKQoKIyBDcmVhdGUgdGhlIGNvbnRpbmdlbmN5IHRhYmxlIGZvciBCTUkgY2F0ZWdvcnkgYW5kIGRpYWJldGVzLCBjb250cm9sbGluZyBmb3IgZ2x1Y29zZSBsZXZlbApjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShoZWFydGRhdGEkQk1JX2NhdGVnb3J5LCBoZWFydGRhdGEkZGlhYmV0ZXMsIGhlYXJ0ZGF0YSRnbHVjb3NlX2xldmVsKQoKIyBEaXNwbGF5IHRoZSBjb250aW5nZW5jeSB0YWJsZQpwcmludChjb250aW5nZW5jeV90YWJsZSkKCgojIENvbmR1Y3QgdGhlIENvY2hyYW4tTWFudGVsLUhhZW5zemVsIHRlc3QKY21oX3Rlc3RfcmVzdWx0IDwtIG1hbnRlbGhhZW4udGVzdChjb250aW5nZW5jeV90YWJsZSkKCiMgUHJpbnQgdGhlIHRlc3QgcmVzdWx0cwpjYXQoIkNvY2hyYW4tTWFudGVsLUhhZW5zemVsIFRlc3QgUmVzdWx0czpcbiIpCnByaW50KGNtaF90ZXN0X3Jlc3VsdCkKCmBgYApUYWJsZSA0LiBUaGUgdGVzdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBnbHVjb3NlIGxldmVsLCB3YXMgc2lnbmlmaWNhbnQuCgoKIyMgQk1JIENhdGVnb3J5IHZzIERpYWJldGVzLCBDb250cm9sIGZvciBBZ2UgR3JvdXAKCkFuYWx5c2lzIG9mIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBhZ2UgZ3JvdXAsIHdhcyBhbHNvIGNvbmR1Y3RlZC4gVGhlIGNvbnRpbmdlbmN5IHRhYmxlcyBhcmUgNFgyIG9yZGVyZWQtbm9taW5hbCBhcnJheXMuIEFsbCBvZiB0aGUgY2VsbCBjb3VudHMgYXJlIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1IHdoZW4gdW5kZXJ3ZWlnaHQgYW5kIGhlYWx0aHlfd2VpZ2h0IGFyZSBjb2xsYXBzZWQgYW5kIGFsbCByb3cgdG90YWxzIGFyZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMjAgd2hlbiBjb21iaW5lZCBvdmVyIGFsbCB0YWJsZXMuIFRoZXJlZm9yZSwgdGhlIHRlc3QgaXMganVzdGlmaWVkLiAKCkhvOiBUaGVyZSBpcyBubyBhc3NvY2lhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMgc3RhdHVzIHdoZW4gY29udHJvbGxpbmcgZm9yIGFnZSBncm91cC4KSGE6IFRoZXJlIGlzIGEgY29ycmVsYXRpb24gYmV0d2VlbiBCTUkgY2F0ZWdvcnkgYW5kIGRpYWJldGVzIHN0YXR1cyB3aGVuIGNvbnRyb2xsaW5nIGZvciBhZ2UgZ3JvdXAuIAoKU2luY2UgdGhlIHAtdmFsdWUgaXMgYXBwcm94aW1hdGVseSB6ZXJvLCB3aGljaCBpcyBsZXNzIHRoYW4gMC4wNSwgd2UgcmVqZWN0IHRoZSBudWxsLiBUaGVyZSBpcyBzdHJvbmcgZXZpZGVuY2Ugb2YgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMgd2hlbiBjb250cm9sbGluZyBmb3IgYWdlIGdyb3VwLiBBcyBCTUkgY2F0ZWdvcnkgaW5jcmVhc2VzLCB0aGUgZGlhYmV0ZXMgc3RhdHVzIGluY3JlYXNlcy4KCmBgYHtyfQoKIyBDb252ZXJ0IEJNSV9jYXRlZ29yeSB0byBhbiBvcmRlcmVkIGZhY3RvcgpoZWFydGRhdGEkQk1JX2NhdGVnb3J5IDwtIGZhY3RvcihoZWFydGRhdGEkQk1JX2NhdGVnb3J5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygidW5kZXJ3ZWlnaHQiLCAiaGVhbHRoeV93ZWlnaHQiLCAib3ZlcndlaWdodCIsICJvYmVzZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRSkKCiMgQ3JlYXRlIHRoZSBjb250aW5nZW5jeSB0YWJsZSBmb3IgQk1JIGNhdGVnb3J5IGFuZCBkaWFiZXRlcywgY29udHJvbGxpbmcgZm9yIGFnZSBncm91cApjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShoZWFydGRhdGEkQk1JX2NhdGVnb3J5LCBoZWFydGRhdGEkZGlhYmV0ZXMsIGhlYXJ0ZGF0YSRhZ2VfZ3JvdXApCgojIERpc3BsYXkgdGhlIGNvbnRpbmdlbmN5IHRhYmxlCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQoKCiMgQ29uZHVjdCB0aGUgQ29jaHJhbi1NYW50ZWwtSGFlbnN6ZWwgdGVzdApjbWhfdGVzdF9yZXN1bHQgPC0gbWFudGVsaGFlbi50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQoKIyBQcmludCB0aGUgdGVzdCByZXN1bHRzCmNhdCgiQ29jaHJhbi1NYW50ZWwtSGFlbnN6ZWwgVGVzdCBSZXN1bHRzOlxuIikKcHJpbnQoY21oX3Rlc3RfcmVzdWx0KQoKYGBgClRhYmxlIDUuIEEgdGVzdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIEJNSSBjYXRlZ29yeSBhbmQgZGlhYmV0ZXMsIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBhZ2UgZ3JvdXAgd2FzIHNpZ25pZmljYW50LgoKCkJhc2VkIG9uIHRoZSBhc3NvY2lhdGlvbiBhbmFseXNpcywgZ2x1Y29zZSBsZXZlbCBpcyBzdHJvbmdseSBjb3JyZWxhdGVkIHdpdGggZGlhYmV0ZXMsIGV2ZW4gd2hlbiBjb250cm9sbGluZyBmb3IgYWdlLCBzaW5jZSBhZ2UgaXMgYXNzb2NpYXRlZCB3aXRoIGRpYWJldGVzLiBBbHNvLCBCTUkgQ2F0ZWdvcnkgaXMgYWxzbyBzdHJvbmdseSBjb3JyZWxhdGVkIHdpdGggZGlhYmV0ZXMsIGV2ZW4gd2hlbiBjb250cm9sbGluZyBmb3IgYWdlIGFuZCBnbHVjb3NlIGxldmVscywgc2luY2UgYm90aCBmYWN0b3JzIGFyZSBhc3NvY2lhdGVkIHdpdGggZGlhYmV0ZXMuIAoKCiMgTE9HSVNUSUMgUkVHUkVTU0lPTgoKIyMgRnVsbCBtb2RlbAoKClRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIGFuYWx5c2lzIHByZWRpY3RzIFRlbiBZZWFyIENIRCwgd2hpY2ggaXMgYSBiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUuIFRoZSBpbml0aWFsIG1vZGVsIGNvbnRhaW5zIGFsbCB0aGUgdmFyaWFibGVzIGluIHRoZSBvcmlnaW5hbCBkYXRhIHN1YnNldC4gCgpgYGB7cn0KCiMgQ29udmVydCBUZW5ZZWFyQ0hEIHRvIGEgZmFjdG9yCmhlYXJ0ZGF0YSRUZW5ZZWFyQ0hEIDwtIGZhY3RvcihoZWFydGRhdGEkVGVuWWVhckNIRCkKCiMgRnVsbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsCmZ1bGwubW9kZWwgPC0gZ2xtKFRlblllYXJDSEQgfiBtYWxlICsgYWdlICsgZWR1Y2F0aW9uICsgY3VycmVudFNtb2tlcisgY2lnc1BlckRheSArIEJQTWVkcyArIHByZXZhbGVudFN0cm9rZSArIHByZXZhbGVudEh5cCArIGRpYWJldGVzICsgdG90Q2hvbCArIEJNSSArIHN5c0JQICsgZGlhQlAgKyBoZWFydFJhdGUrIGdsdWNvc2UsIAogICAgICAgICAgICAgZGF0YSA9IGhlYXJ0ZGF0YSwgCiAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkKCgojIFZpZXcgdGhlIHN1bW1hcnkgb2YgdGhlIGZ1bGwgbW9kZWwKc3VtbWFyeShmdWxsLm1vZGVsKQoKY29lZmZpY2llbnRzIDwtIHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZmZpY2llbnRzCnByaW50X2xvZ2l0X2VxdWF0aW9uIDwtIGZ1bmN0aW9uKGZ1bGwubW9kZWwpIHsKICAgIGNvZWZzIDwtIHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZmZpY2llbnRzCiAgICBpbnRlcmNlcHQgPC0gcm91bmQoY29lZnNbMSwgMV0sIDQpCiAgICB0ZXJtcyA8LSByb3duYW1lcyhjb2VmcylbLTFdCiAgICBlcXVhdGlvbnMgPC0gc2FwcGx5KDE6bGVuZ3RoKHRlcm1zKSwgZnVuY3Rpb24oaSkgewogICAgICAgIHRlcm1fY29lZiA8LSByb3VuZChjb2Vmc1tpICsgMSwgMV0sIDQpCiAgICAgICAgcGFzdGUwKHRlcm1fY29lZiwgIiAqICIsIHRlcm1zW2ldKQogICAgfSkKICAgIGxvZ2l0X2VxdWF0aW9uIDwtIHBhc3RlKCJsb2dpdChwKSA9IiwgaW50ZXJjZXB0LCAiKyIsIHBhc3RlKGVxdWF0aW9ucywgY29sbGFwc2UgPSAiICsgIikpCiAgICByZXR1cm4obG9naXRfZXF1YXRpb24pCn0KCmxvZ2l0X2VxdWF0aW9uIDwtIHByaW50X2xvZ2l0X2VxdWF0aW9uKGZ1bGwubW9kZWwpCmNhdChsb2dpdF9lcXVhdGlvbiwgIlxuIikKYGBgClRoZSBmdWxsIG1vZGVsIGlzIGJlbG93LiAKCmxvZ2l0KHApID0gLTcuOTkyNiArIDAuNDg0MSAqIG1hbGUxICsgMC4wNjA3ICogYWdlICsgLTAuMjI3NyAqIGVkdWNhdGlvbjIgKyAtMC4wMjYyICogZWR1Y2F0aW9uMSArIC0wLjEyNDQgKiBlZHVjYXRpb24zICsgMC4wMTk2ICogY3VycmVudFNtb2tlcjEgKyAwLjAyMTIgKiBjaWdzUGVyRGF5ICsgMC4yNSAqIEJQTWVkczEgKyAwLjk2NzggKiBwcmV2YWxlbnRTdHJva2UxICsgMC4yMzA2ICogcHJldmFsZW50SHlwMSArIDAuMTc5MyAqIGRpYWJldGVzMSArIDAuMDAxOCAqIHRvdENob2wgKyAwLjAwMzQgKiBCTUkgKyAwLjAxNDIgKiBzeXNCUCArIC0wLjAwMzEgKiBkaWFCUCArIC0wLjAwMTIgKiBoZWFydFJhdGUgKyAwLjAwNjcgKiBnbHVjb3NlIAoKCgojIyBUcmltbWVkIE1vZGVsCgpUaGUgZnVsbCBtb2RlbCBoYWQgc2l4IHNpZ25pZmljYW50IHByZWRpY3RvciB2YXJpYWJsZXMgKG1hbGUsIGFnZSwgY2lnc1BlckRheSwgcHJldmFsZW50U3Ryb2tlLCBzeXNCUCwgZ2x1Y29zZSkuIEEgdHJpbW1lZCBtb2RlbCBpcyBjcmVhdGVkIHVzaW5nIHRoZXNlIHZhcmlhYmxlcy4gCgoKYGBge3J9CgojIFRyaW1tZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbAp0cmltbWVkLm1vZGVsIDwtIGdsbShUZW5ZZWFyQ0hEIH4gbWFsZSArIGFnZSArIGNpZ3NQZXJEYXkgKyBwcmV2YWxlbnRTdHJva2UgKyBzeXNCUCArIGdsdWNvc2UsIAogICAgICAgICAgICAgZGF0YSA9IGhlYXJ0ZGF0YSwgCiAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkKCiMgVmlldyB0aGUgc3VtbWFyeSBvZiB0aGUgdHJpbW1lZCBtb2RlbApzdW1tYXJ5KHRyaW1tZWQubW9kZWwpCgpgYGAKCgpUaGUgdHJpbW1lZCBtb2RlbCBpcyBiZWxvdy4KCmxvZ2l0KHApID0gLTguNDU5MSArIDAuNDg1MSAqIG1hbGUxICsgMC4wNjQ4ICogYWdlICsgMC4wMjE0ICogY2lnc1BlckRheSArIDEuMDQ3ICogcHJldmFsZW50U3Ryb2tlMSArIDAuMDE3MSAqIHN5c0JQICsgMC4wMDc2ICogZ2x1Y29zZSAKCgoKVGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIHdoZW4gYWxsIHByZWRpY3RvcnMgYXJlIHplcm8gaXMgLTguNDU5MS4gVGhpcyB2YWx1ZSBpcyBub3QgbWVhbmluZ2Z1bCBpbiBwcmFjdGljYWwgc2Vuc2UuIEl0IHJlcHJlc2VudHMgdGhlIGxvZ3Mgb2RkcyB2YWx1ZSBpZiBhbGwgb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgY2FuIHRha2Ugb24gYSB2YWx1ZSBvZiB6ZXJvLgoKQmVpbmcgbWFsZSAoY29tcGFyZWQgdG8gZmVtYWxlKSBpbmNyZWFzZXMgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGJ5IGFwcHJveGltYXRlbHkgMC40ODUwODguIFRoZSBvZGRzIHJhdGlvIGlzIGFwcHJveGltYXRlbHkgMS42Mi4gVGhpcyBtZWFucyB0aGF0IG1hbGVzIGhhdmUgYWJvdXQgNjIlIGhpZ2hlciBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGNvbXBhcmVkIHRvIGZlbWFsZXMuCgkKRm9yIGVhY2ggYWRkaXRpb25hbCB5ZWFyIGluIGFnZSwgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGluY3JlYXNlIGJ5IGFwcHJveGltYXRlbHkgMC4wNjQ3Ny4gVGhlIG9kZHMgcmF0aW8gaXMgYXBwcm94aW1hdGVseSAxLjA2Ny4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB3aXRoIGVhY2ggYWRkaXRpb25hbCB5ZWFyIG9mIGFnZSwgdGhlIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgaW5jcmVhc2UgYnkgYWJvdXQgNi43JS4KCQpGb3IgZWFjaCBhZGRpdGlvbmFsIGNpZ2FyZXR0ZSBzbW9rZWQgcGVyIGRheSwgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGluY3JlYXNlIGJ5IGFwcHJveGltYXRlbHkgMC4wMjE0MjguIFRoZSBvZGRzIHJhdGlvIGlzIGFwcHJveGltYXRlbHkgMS4wMjEuIFRoaXMgc3VnZ2VzdHMgdGhhdCBlYWNoIGFkZGl0aW9uYWwgY2lnYXJldHRlIHBlciBkYXkgaW5jcmVhc2VzIHRoZSBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGJ5IGFib3V0IDIuMiUuCgkKSGF2aW5nIGEgcHJldmFsZW50IHN0cm9rZSBpbmNyZWFzZXMgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGJ5IGFwcHJveGltYXRlbHkgMS4wNDcwMjQuIFRoZSBvZGRzIHJhdGlvIGlzIGFwcHJveGltYXRlbHkgMi44NS4gVGhpcyBtZWFucyB0aGF0IGluZGl2aWR1YWxzIHdobyBoYXZlIGhhZCBhIHN0cm9rZSBhcmUgYWJvdXQgMi44NSB0aW1lcyBtb3JlIGxpa2VseSB0byBkZXZlbG9wIENIRCBpbiB0aGUgbmV4dCAxMCB5ZWFycyBjb21wYXJlZCB0byB0aG9zZSB3aXRob3V0IGEgc3Ryb2tlLgoJCkZvciBlYWNoIG9uZS11bml0IChtbUhnKSBpbmNyZWFzZSBpbiBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAoc3lzQlApLCB0aGUgbG9nIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgaW5jcmVhc2UgYnkgYXBwcm94aW1hdGVseSAwLjAxNzA2NC4gVGhlIG9kZHMgcmF0aW8gaXMgYXBwcm94aW1hdGVseSAxLjAxNy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCBlYWNoIGFkZGl0aW9uYWwgdW5pdCAobW1IZykgaW5jcmVhc2UgaW4gc3lzQlAgaW5jcmVhc2VzIHRoZSBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGJ5IGFib3V0IDEuNyUuCgkKRm9yIGVhY2ggb25lLXVuaXQgKG1nL2RMKSBpbmNyZWFzZSBpbiBnbHVjb3NlIGxldmVsLCB0aGUgbG9nIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgaW5jcmVhc2UgYnkgYXBwcm94aW1hdGVseSAwLjAwNzU3Ny4gVGhlIG9kZHMgcmF0aW8gaXMgYXBwcm94aW1hdGVseSAxLjAwOC4gVGhpcyBzdWdnZXN0cyB0aGF0IGVhY2ggYWRkaXRpb25hbCB1bml0IGluY3JlYXNlIChtZy9kTCkgaW4gZ2x1Y29zZSBpbmNyZWFzZXMgdGhlIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgYnkgYWJvdXQgMC44JS4KCgpBZ2UgYW5kIGNpZ3NQZXJEYXkgd2VyZSBoaWdobHkgc2lnbmlmaWNhbnQgaW4gdGhlIHRyaW1tZWQgbW9kZWwuIFRoZSBmZWF0dXJlIGVuZ2luZWVyIGludGVncmF0ZXMgYm90aCBvZiB0aGVzZSBmYWN0b3JzLiBJbiB0dXJuLCBhIG1vZGVsIGlzIGNyZWF0ZWQgdXNpbmcgdGhlIGZlYXR1cmUgZW5naW5lZXIsIHNtb2tpbmdfcmlza19pbmRleCBpbiBsaWV1IG9mIGFnZSBhbmQgY2lnc1BlckRheS4gCgpgYGB7cn0KZmUubW9kZWwgPC0gZ2xtKFRlblllYXJDSEQgfiBtYWxlICsgc21va2luZ19yaXNrX2luZGV4ICsgcHJldmFsZW50U3Ryb2tlICsgc3lzQlAgKyBnbHVjb3NlLCAKICAgICAgICAgICAgIGRhdGEgPSBoZWFydGRhdGEsIAogICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwpCgpzdW1tYXJ5KGZlLm1vZGVsKQoKYGBgCkFsbCB2YXJpYWJsZXMgYXJlIHNpZ25pZmljYW50LiBUaGlzIG1vZGVsIGhhcyBmaXZlIHZhcmlhYmxlcywgb25lIGxlc3MgdGhhbiB0aGUgdHJpbW1lZCBtb2RlbC4gCgpsb2dpdChwKSA9IC03LjI0NzcgKyAwLjYxMTEgKiBtYWxlMSArIDAuMDE1NTAgKiBzbW9raW5nX3Jpc2tfaW5kZXggKyAxLjIxMzQgKiBwcmV2YWxlbnRTdHJva2UxICsgMC4wMjIwICogc3lzQlAgKyAwLjAwODAgKiBnbHVjb3NlCgoKVGhlIGludGVyY2VwdCBvZiAtNy4yNDc3IHJlcHJlc2VudHMgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIHdoZW4gYWxsIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIGVxdWFsIHRvIHplcm8uIEhvd2V2ZXIsIHRoZSBpbnRlcmNlcHQgdmFsdWUgaXMgbm90IG1lYW5pbmdmdWwgaW4gdGhpcyBjb250ZXh0LgoKVGhlIGNvZWZmaWNpZW50IGZvciBtYWxlMSBpcyAwLjYxMTEyOS4gVGhpcyBpbmRpY2F0ZXMgdGhhdCBiZWluZyBtYWxlIChjb21wYXJlZCB0byBmZW1hbGUpIGlzIGFzc29jaWF0ZWQgd2l0aCBhbiBpbmNyZWFzZSBpbiB0aGUgbG9nIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhci4gVGhlIG9kZHMgcmF0aW8gaXMgYXBwcm94aW1hdGVseSAxLjg0LiBUaGlzIG1lYW5zIHRoYXQgbWFsZXMgaGF2ZSBhYm91dCAxLjg0IHRpbWVzIHRoZSBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGNvbXBhcmVkIHRvIGZlbWFsZXMsIGhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4KClRoZSBjb2VmZmljaWVudCBmb3Igc21va2luZ19yaXNrX2luZGV4IGlzIDAuMDE1NDk5LiBUaGlzIHN1Z2dlc3RzIHRoYXQgZm9yIGVhY2ggb25lLXVuaXQgaW5jcmVhc2UgaW4gdGhlIHNtb2tpbmcgcmlzayBpbmRleCwgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXIgaW5jcmVhc2UgYnkgMC4wMTU0OTkuIFRoZSBvZGRzIHJhdGlvIGlzIDEuMDE1NDk5LiBUaGlzIG1lYW5zIHRoYXQgZm9yIGVhY2ggYWRkaXRpb25hbCBwb2ludCBpbiB0aGUgc21va2luZyByaXNrIGluZGV4LCB0aGUgb2RkcyBvZiBkZXZlbG9waW5nIENIRCBpbiB0aGUgbmV4dCAxMCB5ZWFycyBpbmNyZWFzZSBieSBhcHByb3hpbWF0ZWx5IDEuNSUuCgpUaGUgY29lZmZpY2llbnQgZm9yIHByZXZhbGVudFN0cm9rZTEgaXMgMS4yMTMzNTEuIFRoaXMgaW5kaWNhdGVzIHRoYXQgaGF2aW5nIGEgcHJldmFsZW50IHN0cm9rZSBpcyBhc3NvY2lhdGVkIHdpdGggYW4gaW5jcmVhc2UgaW4gdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGJ5IDEuMjEzMzUxLiBUaGUgb2RkcyByYXRpbyBpcyBhcHByb3hpbWF0ZWx5IDMuMzYuIFRoaXMgbWVhbnMgdGhhdCBpbmRpdmlkdWFscyB3aXRoIGEgcHJldmFsZW50IHN0cm9rZSBoYXZlIGFib3V0IDMuMzYgdGltZXMgdGhlIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgY29tcGFyZWQgdG8gdGhvc2Ugd2l0aG91dC4KClRoZSBjb2VmZmljaWVudCBmb3Igc3lzQlAgaXMgMC4wMjIwMzUuIFRoaXMgbWVhbnMgdGhhdCBmb3IgZWFjaCBvbmUtdW5pdCAobW1IZykgaW5jcmVhc2UgaW4gc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIHRoZSBsb2cgb2RkcyBvZiBkZXZlbG9waW5nIENIRCBpbiB0aGUgbmV4dCAxMCB5ZWFycyBpbmNyZWFzZSBieSAwLjAyMjAzNS4gVGhlIG9kZHMgcmF0aW8gaXMgYXBwcm94aW1hdGVseSAxLjAyMjMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBmb3IgZWFjaCB1bml0IChtbUhnKSBpbmNyZWFzZSBpbiBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgdGhlIG9kZHMgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMgaW5jcmVhc2UgYnkgYXBwcm94aW1hdGVseSAyLjIlLgoKVGhlIGNvZWZmaWNpZW50IGZvciBnbHVjb3NlIGlzIDAuMDA3OTgyLiBUaGlzIGluZGljYXRlcyB0aGF0IGZvciBlYWNoIG9uZS11bml0IChtZy9kTCkgaW5jcmVhc2UgaW4gZ2x1Y29zZSBsZXZlbCwgdGhlIGxvZyBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGluY3JlYXNlIGJ5IDAuMDA3OTgyLiBUaGUgb2RkcyByYXRpbyBpcyBhcHByb3hpbWF0ZWx5IDEuMDA4LiBUaGlzIG1lYW5zIHRoYXQgZm9yIGVhY2ggdW5pdCAobWcvZEwpIGluY3JlYXNlIGluIGdsdWNvc2UsIHRoZSBvZGRzIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzIGluY3JlYXNlIGJ5IGFib3V0IDAuOCUuCgoKCiMgTE9HSVNUSUMgTU9ERUwgRElBR05PU1RJQ1MKClRoZSB0cmltbWVkIG1vZGVsIGFuZCB0aGUgZmVhdHVyZSBlbmdpbmVlciBtb2RlbCB3ZXJlIGNvbXBhcmVkIHVzaW5nIFJPQyBjdXJ2ZS4gVGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlIGZvciB0aGUgdHJpbW1lZCBtb2RlbCwgMC43MjksIGlzIGdyZWF0ZXIgdGhhbiB0aGUgYXJlYSB1bmRlciB0aGUgY3VydmUgZm9yIHRoZSBmZWF0dXJlIGVuZ2luZWVyIG1vZGVsLCAwLjYyOS4gCgpgYGB7cn0KIyBHZXQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMKdHJpbW1lZF9wcm9iIDwtIHByZWRpY3QodHJpbW1lZC5tb2RlbCwgdHlwZSA9ICJyZXNwb25zZSIpCmZlX3Byb2IgPC0gcHJlZGljdChmZS5tb2RlbCwgdHlwZSA9ICJyZXNwb25zZSIpCgojIENhbGN1bGF0ZSByZXNpZHVhbHMKdHJpbW1lZF9yZXNpZHVhbHMgPC0gcmVzaWR1YWxzKHRyaW1tZWQubW9kZWwsIHR5cGUgPSAiZGV2aWFuY2UiKQpmZV9yZXNpZHVhbHMgPC0gcmVzaWR1YWxzKGZlLm1vZGVsLCB0eXBlID0gImRldmlhbmNlIikKCgojIFJPQyBDdXJ2ZSBmb3IgdHJpbW1lZC5tb2RlbApyb2NfdHJpbW1lZCA8LSByb2MoaGVhcnRkYXRhJFRlblllYXJDSEQsIHRyaW1tZWRfcHJvYikKcGxvdChyb2NfdHJpbW1lZCwgbWFpbiA9ICJST0MgQ3VydmUgZm9yIFRyaW1tZWQgTW9kZWwiKQphdWMocm9jX3RyaW1tZWQpCgojIFJPQyBDdXJ2ZSBmb3IgZmUubW9kZWwKcm9jX2ZlIDwtIHJvYyhoZWFydGRhdGEkVGVuWWVhckNIRCwgZmVfcHJvYikKcGxvdChyb2NfZmUsIG1haW4gPSAiUk9DIEN1cnZlIGZvciBGZWF0dXJlIEVuZ2luZWVyZWQgTW9kZWwiKQphdWMocm9jX2ZlKQoKCmBgYApGaWd1cmUgSEguIFRoZSB0cmltbWVkIG1vZGVsIHBlcmZvcm1lZCBiZXR0ZXIgdGhhbiB0aGUgZmVhdHVyZSBlbmdpbmVlciBtb2RlbC4gCgoKQWNjdXJhY3ksIHNlbnNpdGl2aXR5LCBhbmQgc3BlY2lmaWNpdHkgd2VyZSBjb21wYXJlZCBmb3IgYm90aCBtb2RlbHMgYXMgcGFydCBvZiB0aGUgZGlhZ25vc3RpY3MuIFRhYmxlIDYgc3VtbWFyaXplcyB0aGUgZmluZGluZ3MuIAoKMS4gQWNjdXJhY3kKCgnigKIJVHJpbW1lZCBNb2RlbDogODUuNDclCgnigKIJRmVhdHVyZSBFbmdpbmVlcmVkIE1vZGVsOiA4NS4wNSUKCkFjY3VyYWN5IHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgcHJvcG9ydGlvbiBvZiBjb3JyZWN0IHByZWRpY3Rpb25zIChib3RoIHRydWUgcG9zaXRpdmVzIGFuZCB0cnVlIG5lZ2F0aXZlcykgb3V0IG9mIGFsbCBwcmVkaWN0aW9ucyBtYWRlLiBJbiBib3RoIG1vZGVscywgYWNjdXJhY3kgaXMgaGlnaCAoYXJvdW5kIDg1JSksIG1lYW5pbmcgdGhleSBjb3JyZWN0bHkgY2xhc3NpZnkgYWJvdXQgODUlIG9mIGFsbCBjYXNlcy4KCjIuIFNlbnNpdGl2aXR5IChSZWNhbGwpCgoJ4oCiCVRyaW1tZWQgTW9kZWw6IDguMjMlCgnigKIJRmVhdHVyZSBFbmdpbmVlcmVkIE1vZGVsOiA0LjgxJQoKU2Vuc2l0aXZpdHkgKG9yIHJlY2FsbCkgbWVhc3VyZXMgaG93IHdlbGwgdGhlIG1vZGVsIGlkZW50aWZpZXMgYWN0dWFsIHBvc2l0aXZlcyAoVGVuWWVhckNIRCA9IDEpIHdoaWNoIGlzIHRoZSBwcm9wb3J0aW9uIG9mIHRydWUgcG9zaXRpdmVzIG91dCBvZiBhbGwgYWN0dWFsIHBvc2l0aXZlcyAoVFAgLyBbVFAgKyBGTl0pLgoKCeKAoglUcmltbWVkIE1vZGVsOiBUaGlzIG1vZGVsIG9ubHkgaWRlbnRpZmllcyBhYm91dCA4LjIzJSBvZiBhY3R1YWwgcG9zaXRpdmUgY2FzZXMuIFRoZSBtb2RlbCBzdHJ1Z2dsZXMgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG1vc3QgY2FzZXMgd2hlcmUgVGVuWWVhckNIRCA9IDEuCgnigKIJRmVhdHVyZSBFbmdpbmVlcmVkIE1vZGVsOiBTZW5zaXRpdml0eSBpcyBldmVuIGxvd2VyIGF0IDQuODElLCBpbmRpY2F0aW5nIGFuIGV2ZW4gZ3JlYXRlciBkaWZmaWN1bHR5IGluIGlkZW50aWZ5aW5nIHRydWUgcG9zaXRpdmVzICh3aGVuIFRlblllYXJDSEQgPSAxKS4KCjMuIFNwZWNpZmljaXR5CgoJ4oCiCVRyaW1tZWQgTW9kZWw6IDk5LjMlCgnigKIJRmVhdHVyZSBFbmdpbmVlcmVkIE1vZGVsOiA5OS40MiUKClNwZWNpZmljaXR5IG1lYXN1cmVzIHRoZSBtb2RlbOKAmXMgYWJpbGl0eSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgYWN0dWFsIG5lZ2F0aXZlcyAoY29ycmVjdGx5IHByZWRpY3RpbmcgY2FzZXMgd2hlcmUgVGVuWWVhckNIRCA9IDApLiBJdOKAmXMgdGhlIHByb3BvcnRpb24gb2YgdHJ1ZSBuZWdhdGl2ZXMgb3V0IG9mIGFsbCBhY3R1YWwgbmVnYXRpdmVzIChUTiAvIFtUTiArIEZQXSkuCgoJ4oCiCVRyaW1tZWQgTW9kZWw6IDk5LjI4JSBvZiBhY3R1YWwgbmVnYXRpdmUgY2FzZXMgYXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkLCBtZWFuaW5nIGl04oCZcyBoaWdobHkgZWZmZWN0aXZlIGF0IGlkZW50aWZ5aW5nIGNhc2VzIHdoZXJlIFRlblllYXJDSEQgPSAwLgoJ4oCiCUZlYXR1cmUgRW5naW5lZXJlZCBNb2RlbDogOTkuNDIlLCBldmVuIGJldHRlciBhdCBpZGVudGlmeWluZyBuZWdhdGl2ZXMsIGJ1dCBvbmx5IHNsaWdodGx5IGhpZ2hlci4KCgpCb3RoIG1vZGVscyBoYXZlIGhpZ2ggc3BlY2lmaWNpdHkgYW5kIGFyZSB2ZXJ5IGdvb2QgYXQgaWRlbnRpZnlpbmcgY2FzZXMgd2hlcmUgVGVuWWVhckNIRCA9IDAuIEhvd2V2ZXIsIGJvdGggbW9kZWxzIGhhdmUgbG93IHNlbnNpdGl2aXR5LCBtZWFuaW5nIHRoZXkgYXJlIHBvb3IgYXQgaWRlbnRpZnlpbmcgY2FzZXMgd2hlcmUgVGVuWWVhckNIRCA9IDEuIFRoaXMgbWF5IGhhdmUgb2NjdXJyZWQgYmVjYXVzZSBsZXNzIHRoYW4gMjAlIG9mIG9ic2VydmF0aW9ucyBoYWQgVGVuWWVhckNIRCA9IDEgKHBvc2l0aXZlIGNhc2UpLiAKCgpgYGB7cn0KCiMgQXNzdW1pbmcgeW91IGhhdmUgeW91ciBtb2RlbHMgdHJpbW1lZC5tb2RlbCBhbmQgZmUubW9kZWwgYWxyZWFkeSBkZWZpbmVkCgojIFN0ZXAgMTogUHJlZGljdCBwcm9iYWJpbGl0aWVzIGZvciBib3RoIG1vZGVscwpoZWFydGRhdGEkcHJlZGljdGVkX3RyaW1tZWQgPC0gcHJlZGljdCh0cmltbWVkLm1vZGVsLCBuZXdkYXRhID0gaGVhcnRkYXRhLCB0eXBlID0gInJlc3BvbnNlIikKaGVhcnRkYXRhJHByZWRpY3RlZF9mZSA8LSBwcmVkaWN0KGZlLm1vZGVsLCBuZXdkYXRhID0gaGVhcnRkYXRhLCB0eXBlID0gInJlc3BvbnNlIikKCiMgU3RlcCAyOiBDb252ZXJ0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHRvIGJpbmFyeSBvdXRjb21lcyAoMCBvciAxKQpoZWFydGRhdGEkcHJlZGljdGVkX3RyaW1tZWRfb3V0Y29tZSA8LSBpZmVsc2UoaGVhcnRkYXRhJHByZWRpY3RlZF90cmltbWVkID4gMC41LCAxLCAwKQpoZWFydGRhdGEkcHJlZGljdGVkX2ZlX291dGNvbWUgPC0gaWZlbHNlKGhlYXJ0ZGF0YSRwcmVkaWN0ZWRfZmUgPiAwLjUsIDEsIDApCgojIFN0ZXAgMzogQ29tcGFyZSBwcmVkaWN0ZWQgb3V0Y29tZXMgdG8gYWN0dWFsIFRlblllYXJDSEQgdmFsdWVzCiMgQ2FsY3VsYXRlIGFjY3VyYWN5LCBzZW5zaXRpdml0eSwgc3BlY2lmaWNpdHksIGV0Yy4sIGZvciBib3RoIG1vZGVscwoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgbWV0cmljcwpjYWxjdWxhdGVfbWV0cmljcyA8LSBmdW5jdGlvbihhY3R1YWwsIHByZWRpY3RlZCkgewogIGNvbmZ1c2lvbl9tYXRyaXggPC0gdGFibGUoYWN0dWFsLCBwcmVkaWN0ZWQpCiAgYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZnVzaW9uX21hdHJpeCkpIC8gc3VtKGNvbmZ1c2lvbl9tYXRyaXgpCiAgc2Vuc2l0aXZpdHkgPC0gY29uZnVzaW9uX21hdHJpeFsyLCAyXSAvIHN1bShjb25mdXNpb25fbWF0cml4WzIsIF0pCiAgc3BlY2lmaWNpdHkgPC0gY29uZnVzaW9uX21hdHJpeFsxLCAxXSAvIHN1bShjb25mdXNpb25fbWF0cml4WzEsIF0pCiAgCiAgbGlzdChhY2N1cmFjeSA9IGFjY3VyYWN5LCBzZW5zaXRpdml0eSA9IHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5KQp9CgojIE1ldHJpY3MgZm9yIHRyaW1tZWQubW9kZWwKbWV0cmljc190cmltbWVkIDwtIGNhbGN1bGF0ZV9tZXRyaWNzKGhlYXJ0ZGF0YSRUZW5ZZWFyQ0hELCBoZWFydGRhdGEkcHJlZGljdGVkX3RyaW1tZWRfb3V0Y29tZSkKY2F0KCJUcmltbWVkIE1vZGVsIE1ldHJpY3M6XG4iKQpwcmludChtZXRyaWNzX3RyaW1tZWQpCgojIE1ldHJpY3MgZm9yIGZlLm1vZGVsCm1ldHJpY3NfZmUgPC0gY2FsY3VsYXRlX21ldHJpY3MoaGVhcnRkYXRhJFRlblllYXJDSEQsIGhlYXJ0ZGF0YSRwcmVkaWN0ZWRfZmVfb3V0Y29tZSkKY2F0KCJGZWF0dXJlIEVuZ2luZWVyZWQgTW9kZWwgTWV0cmljczpcbiIpCnByaW50KG1ldHJpY3NfZmUpCgpgYGAKVGFibGUgNi4gQm90aCBtb2RlbHMgaGF2ZSBzaW1pbGFyIGFjY3VyYWN5IGF0IGFib3V0IDg1JS4gSG93ZXZlciwgdGhlIHRyaW1tZWQgbW9kZWwgaGFkIGhpZ2hlciBzZW5zaXRpdml0eSB0aGFuIHRoZSBmZWF0dXJlIGVuZ2luZWVyIG1vZGVsLiBUaGUgZmVhdHVyZSBlbmdpbmVlciBtb2RlbCBoYWQgc2xpZ2h0bHkgaGlnaGVyIHNwZWNpZmljaXR5IHRoYW4gdGhlIHRyaW1tZWQgbW9kZWwuIAoKCiMgQ09OQ0xVU0lPTgoKTW9zdCBvZiB0aGUgdmFyaWFibGVzIGluIHRoaXMgRnJhbWluZ2hhbSBIZWFydCBTdHVkeSBkYXRhIHN1YnNldCBhcmUga25vd24gdG8gYmUgYXNzb2NpYXRlZCB3aXRoIGhlYXJ0IGRpc2Vhc2UsIHNwZWNpZmljYWxseSBDSEQuIEFzIHBlb3BsZSBhZ2UsIHRoZSByaXNrIG9mIGRldmVsb3BpbmcgaGVhcnQgZGlzZWFzZSBpbmNyZWFzZXMgZHVlIHRvIHZhcmlvdXMgYmlvbG9naWNhbCBhbmQgYmVoYXZpb3JhbCBmYWN0b3JzLiBIaWdoIGJsb29kIHByZXNzdXJlIGRhbWFnZXMgdGhlIGhlYXJ0IGFuZCBibG9vZCB2ZXNzZWxzLCBpbmNyZWFzaW5nIHRoZSByaXNrIG9mIGhlYXJ0IGRpc2Vhc2UuIEluZGl2aWR1YWxzIHRoYXQgYXJlIG9uIGJsb29kIHByZXNzdXJlIG1lZGljYXRpb24sIGEgdmFyaWFibGUgaW4gdGhpcyBzdHVkeSwgaXMgYW4gaW5kaWNhdGlvbiBvZiBoaWdoIGJsb29kIHByZXNzdXJlLiBBIGhpZ2hlciByZXN0aW5nIGhlYXJ0IHJhdGUgaXMgYXNzb2NpYXRlZCB3aXRoIGhlYXJ0IGRpc2Vhc2UgYmVjYXVzZSB0aGUgaGVhcnQgaXMgZW5kdXJpbmcgYSBoaWdoZXIgd29ya2xvYWQuIENob2xlc3Rlcm9sIGxldmVscyBhcmUgYXNzb2NpYXRlZCB3aXRoIGRldmVsb3BpbmcgQ0hELiBTcGVjaWZpY2FsbHksIGhpZ2ggbGV2ZWxzIG9mIExETCBjaG9sZXN0ZXJvbCBpcyBzdHJvbmdseSBhc3NvY2lhdGVkIHdpdGggaGVhcnQgZGlzZWFzZS4gSERMIGNob2xlc3Rlcm9sIGFyZSBwcm90ZWN0aXZlIGFnYWluc3QgaGVhcnQgZGlzZWFzZS4gSG93ZXZlciwgdGhlIEhETCB2YXJpYWJsZSB3YXMgbm90IGluY2x1ZGVkIGluIHRoaXMgZGF0YSBzdWJzZXQuIFNtb2tpbmcgZGFtYWdlcyBibG9vZHMgdmVzc2VscyB3aGljaCBpbmNyZWFzZXMgdGhlIHJpc2sgb2YgaGVhcnQgZGlzZWFzZS4gSGlnaCBibG9vZCBnbHVjb3NlIGxldmVscyAoaW5kaWNhdGlvbiBvZiBwb3NzaWJsZSBkaWFiZXRlcyksIGNhbiBkYW1hZ2UgYmxvb2QgdmVzc2VscyBhbmQgbmVydmVzIGNvbnRyb2xsaW5nIHRoZSBoZWFydC4gSGlnaCBCTUkgaW5kaWNhdGVzIGV4Y2VzcyB3ZWlnaHQgd2hpY2ggaXMgYXNzb2NpYXRlZCB3aXRoIGluY3JlYXNlZCBibG9vZCBwcmVzc3VyZSwgY2hvbGVzdGVyb2wgbGV2ZWxzLCBhbmQgcmlzayBvZiBkaWFiZXRlcyB3aGljaCBhcmUgYWxsIGZhY3RvcnMgb2YgQ0hELiBNYWxlcyBoYXZlIGEgaGlnaGVyIG92ZXJhbGwgcmlzayBvZiBkZXZlbG9waW5nIGhlYXJ0IGRpc2Vhc2UgZWFybGllciB0aGFuIGZlbWFsZXMgZHVlIHRvIHZhcmlvdXMgcmVhc29ucyBpbmNsdWRpbmcgaG9ybW9uZXMgYW5kIGdlbmV0aWNzLiAoaHR0cHM6Ly93d3cuY2RjLmdvdi9oZWFydC1kaXNlYXNlL3Jpc2stZmFjdG9ycy9pbmRleC5odG1sKS4gTWFueSBvZiB0aGVzZSBmaW5kaW5ncyBhcmUgdmFsaWRhdGVkIGluIHRoaXMgYW5hbHlzaXMuCgpUaGUgZ29hbCBvZiB0aGlzIGFuYWx5c2lzIHdhcyB0byBmaW5kIHNpZ25pZmljYW50IGFzc29jaWF0aW9ucyBhbmQgcHJlZGljdG9ycyBmb3IgQ0hELiBTaWduaWZpY2FudCBldmlkZW5jZSBvZiBhc3NvY2lhdGlvbiBiZXR3ZWVuIHNleCBhbmQgVGVuWWVhckNIRCB3YXMgZXN0YWJsaXNoZWQgdGhyb3VnaCBBbmFseXNpcyBvZiBWYXJpYW5jZS4gSW4gZmFjdCwgbWFsZXMgaGFkIGhpZ2hlciBvdXRjb21lcyBvZiBUZW5ZZWFyQ0hEIHRoYW4gZmVtYWxlcy4gQXMgd2VsbCwgc2lnbmlmaWNhbnQgZXZpZGVuY2Ugb2YgYXNzb2NpYXRpb24gd2FzIGZvdW5kIGZvciBibG9vZCBwcmVzc3VyZSwgYm90aCBzeXN0b2xpYyBhbmQgZGlhc3RvbGljLCBmb3Igc21va2VycyBhbmQgbm9uLXNtb2tlcnMuIFNtb2tlcnMgaGF2ZSBsb3dlciBibG9vZCBwcmVzc3VyZSBsaWtlbHkgZHVlIHRvIHRoZSB2YXNvZGlsYXRvcnkgZWZmZWN0cyBvZiBuaWNvdGluZS4gCgpBIENoaSBTcXVhcmUgZ2VuZXJhbCB0ZXN0IG9mIGFzc29jaWF0aW9uIGJldHdlZW4gc2V4IGFuZCBUZW5ZZWFyQ0hEIGluZGljYXRlZCBhIHNpZ25pZmljYW50IGFzc29jaWF0aW9uLiBJbiBmYWN0LCBtZW4gdGVuZCB0byBoYXZlIHBvc2l0aXZlIFRlblllYXJDSEQgb3V0Y29tZXMgbW9yZSB0aGFuIHdvbWVuLiBBIGdlbmVyYWwgdGVzdHMgb2YgYXNzb2NpYXRpb24gcmVzdWx0ZWQgaW4gc2lnbmlmaWNhbnQgY29ycmVsYXRpb25zIGJldHdlZW4gZ2x1Y29zZSBsZXZlbCBhbmQgZGlhYmV0ZXMgd2hpbGUgY29udHJvbGxpbmcgZm9yIGFnZS4gQXMgd2VsbCwgQk1JIGFuZCBkaWFiZXRlcyBoYWQgc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gd2hpbGUgY29udHJvbGxpbmcgZm9yIGdsdWNvc2UgYW5kIGFnZS4gCgpMb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyB3ZXJlIGNyZWF0ZWQgdG8gcHJlZGljdCBUZW5ZZWFyQ0hELiBUaGUgdHJpbW1lZCBtb2RlbCBoYWQgdGhlIGJlc3QgcGVyZm9ybWFuY2UgdXNpbmcgc2V4LCBhZ2UsIGNpZ2FyZXR0ZXMgcGVyIGRheSwgcHJldmFsZW50IHN0cm9rZSwgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGFuZCBnbHVjb3NlIGFzIGZhY3RvcnMuIFRoZSBtb2RlbHMgZGlkIG5vdCBwcmVkaWN0IHRoZSBwb3NpdGl2ZSBvdXRjb21lcyB2ZXJ5IHdlbGwuIEFjY29yZGluZyB0byB0aGUgRnJhbWluZ2hhbSBIZWFydCBTdHVkeSwgdGhlIHByZWRpY3RvcnMgdXNlZCBpbiB0aGVpciBtb2RlbCBpbmNsdWRlcyBhZ2UsIHNleCwgcmFjZSwgdG90YWwgY2hvbGVzdGVyb2wsIEhETCBjaG9sZXN0ZXJvbCwgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGJsb29kIHByZXNzdXJlIGxvd2VyaW5nIG1lZGljYXRpb24gdXNlLCBkaWFiZXRlcywgYW5kIHNtb2tpbmcuICAoaHR0cHM6Ly93d3cuZnJhbWluZ2hhbWhlYXJ0c3R1ZHkub3JnL2Zocy1yaXNrLWZ1bmN0aW9ucy9jYXJkaW92YXNjdWxhci1kaXNlYXNlLTEwLXllYXItcmlzay8pLiBUd28gb2YgdGhlIGZhY3RvcnMsIHJhY2UgYW5kIEhETCBjaG9sZXN0ZXJvbCwgd2VyZSBub3QgYXZhaWxhYmxlIGluIHRoZSBkYXRhIHN1YnNldCB1c2VkIGZvciB0aGlzIGFuYWx5c2lzLiBUaGVzZSBtaXNzaW5nIGZhY3RvcnMgbWF5IGNvbnRyaWJ1dGUgdG8gbG93IHNlbnNpdGl2aXR5LiBBcyB3ZWxsLCB0aGUgYWNjdXJhY3kgaXMgZXh0cmVtZWx5IGhpZ2ggcG9zc2libHkgYmVjYXVzZSB0aGUgbW9kZWwgaXMgcHJlZG9taW5hbnRseSBwcmVkaWN0aW5nIHRoZSBtYWpvcml0eSBncm91cCBvZiBUZW5ZZWFyQ0hELCB3aGljaCBpcyB0aGUgbmVnYXRpdmUgY2FzZXMuIAoKT3ZlcmFsbCwga2V5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBmYWN0b3JzIHdlcmUgY29uZmlybWVkIGluIHRoaXMgYW5hbHlzaXMuIEJlbmNobWFya2luZyBhZ2FpbnN0IHRoZSBGcmFtaW5naGFtIDEwLXllYXIgcmlzayBjYWxjdWxhdG9yIChodHRwczovL3d3dy5mcmFtaW5naGFtaGVhcnRzdHVkeS5vcmcvZmhzLXJpc2stZnVuY3Rpb25zL2NhcmRpb3Zhc2N1bGFyLWRpc2Vhc2UtMTAteWVhci1yaXNrLyksIGZhY3RvcnMgd2VyZSBzdWNjZXNzZnVsbHkgaWRlbnRpZmllZCBpbiB0aGlzIGFuYWx5c2lzIGFzIGJlaW5nIHNpZ25pZmljYW50IHByZWRpY3RvcnMgb2YgVGVuWWVhckNIRC4gIAoKCiMgV09SS1MgQ0lURUQKCkNlbnRlcnMgZm9yIERpc2Vhc2UgQ29udHJvbCBhbmQgUHJldmVudGlvbi4gKG4uZC4pLiBEZWZpbmluZyBhZHVsdCBvdmVyd2VpZ2h0ICYgb2Jlc2l0eS4gVS5TLiBEZXBhcnRtZW50IG9mIEhlYWx0aCBhbmQgSHVtYW4gU2VydmljZXMuIGh0dHBzOi8vd3d3LmNkYy5nb3YvYm1pL2FkdWx0LWNhbGN1bGF0b3IvYm1pLWNhdGVnb3JpZXMuaHRtbAoKQ2VudGVycyBmb3IgRGlzZWFzZSBDb250cm9sIGFuZCBQcmV2ZW50aW9uLiAobi5kLikuIERpYWJldGVzIHRlc3RzLiBVLlMuIERlcGFydG1lbnQgb2YgSGVhbHRoIGFuZCBIdW1hbiBTZXJ2aWNlcy4gaHR0cHM6Ly93d3cuY2RjLmdvdi9kaWFiZXRlcy9kaWFiZXRlcy10ZXN0aW5nL2luZGV4Lmh0bQoKQ2VudGVycyBmb3IgRGlzZWFzZSBDb250cm9sIGFuZCBQcmV2ZW50aW9uLiAoMjAyMywgQXByaWwgMTMpLiBSaXNrIGZhY3RvcnMgZm9yIGhlYXJ0IGRpc2Vhc2UuIGh0dHBzOi8vd3d3LmNkYy5nb3YvaGVhcnQtZGlzZWFzZS9yaXNrLWZhY3RvcnMvaW5kZXguaHRtbAoKRGF2aWVzIEIuIEhlYWx0aGNhcmUgUHJpb3JpdGllczogVGhlICJZb3VuZyIgYW5kIHRoZSAiT2xkIi4gQ2FtYiBRIEhlYWx0aGMgRXRoaWNzLiAyMDIyIE5vdiAxMDszMigyKToxLTEyLiBkb2k6IDEwLjEwMTcvUzA5NjMxODAxMjIwMDAyOTkuIEVwdWIgYWhlYWQgb2YgcHJpbnQuIFBNSUQ6IDM2MzUyNzcwOyBQTUNJRDogUE1DMTA0MjU5MjEuCgpGcmFtaW5naGFtIEhlYXJ0IFN0dWR5LiAobi5kLikuIENhcmRpb3Zhc2N1bGFyIGRpc2Vhc2UgMTAteWVhciByaXNrLiBSZXRyaWV2ZWQgT2N0b2JlciAyMiwgMjAyNCwgZnJvbSBodHRwczovL3d3dy5mcmFtaW5naGFtaGVhcnRzdHVkeS5vcmcvZmhzLXJpc2stZnVuY3Rpb25zL2NhcmRpb3Zhc2N1bGFyLWRpc2Vhc2UtMTAteWVhci1yaXNrLwoKU2hhcm1hLCBSLiwgS2F1ciwgUi4sIEthdXIsIEcuLCBTaW5naCwgSy4sIFByYWJoYWthciwgQS4sIFNoYXJtYSwgQS4sICYgS2F1ciwgQS4gKDIwMjIpLiBFbWVyZ2luZyByb2xlcyBvZiBndXQgbWljcm9iaW90YSBpbiBoZWFsdGggYW5kIGRpc2Vhc2U6IEEgcmV2aWV3LiBGcm9udGllcnMgaW4gTWljcm9iaW9sb2d5LCAxMywgQXJ0aWNsZSA3OTk4NDguIGh0dHBzOi8vZG9pLm9yZy8xMC4zMzg5L2ZtaWNiLjIwMjIuNzk5ODQ4Cgo=